I want to have various "flavors" of a component, each that handles a different "wire" format (e.g. String, Byte array, etc.). Example below. The innards of the read() function aren't important.
Note that on use I need to cast parameter "Heavy" to thing.WIRE to work. Since this is my top-level API I don't want the users to have to cast. They've chosen the flavor when they call FantasticThing.apply (or accept the default). After that I'd rather a cast not be needed.
How can I avoid the cast and have Scala realize that read() argument is a String based on StringFlavor being chosen?
trait Flavor {
type WIRE
def read[T](wire: WIRE)(implicit tt: TypeTag[T]): T
}
trait Maker {
def make(): Flavor
}
object StringFlavor extends Maker {
def make(): Flavor { type WIRE = String } = StringFlavor()
}
case class StringFlavor() extends Flavor {
type WIRE = String
def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
println(tt.tpe)
if(tt.tpe =:= typeOf[Int]) {
5.asInstanceOf[T]
} else
throw new Exception("Boom")
}
}
object FantasticThing {
def apply[WIRE](maker: Maker = StringFlavor): Flavor = maker.make()
}
object RunMe extends App {
val thing: Flavor = FantasticThing(StringMaker)
println(thing.read[Int]("Heavy".asInstanceOf[thing.WIRE])) // <-- How can I avoid this cast?
}
Edit based on Luis Miguel's note: I can't really add that type to FantasticThing.apply() or I'd lose pluggability. I want users to easily select the Flavor they want. I've refactored a bit to show this with a factory pattern, which does add the type info you suggested, but still unfortunately leaves me with a need to cast top-level.
If I provide a bunch of Flavors then users should be able to do something like:
val foo = FantasticThing(ByteArrayFlavor)
You can make WIRE a type parameter and propagate it through a type member or your Maker type. I.e:
import scala.reflect.runtime.universe._
trait Flavor[WIRE] {
def read[T](wire: WIRE)(implicit tt: TypeTag[T]): T
}
trait Maker {
type O
def make(): Flavor[O]
}
object StringMaker extends Maker {
type O = String
def make(): Flavor[O] = StringFlavor()
}
case class StringFlavor() extends Flavor[String] {
def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
if(tt.tpe =:= typeOf[Int]) {
5.asInstanceOf[T]
} else
throw new Exception("Boom")
}
}
object FantasticThing {
def apply(): Flavor[String] = StringMaker.make()
def apply(maker: Maker): Flavor[maker.O] = maker.make() // Path dependent type.
}
object RunMe extends App {
val thing: Flavor[String] = FantasticThing(StringMaker)
thing.read[Int]("Heavy") // res0: Int = 5
}
Edit: Added no-arg apply() to this anwser. If a default value for maker is used (e.g. StringMaker) you get a compile error because argument "Heavy" is now supposed to be type Maker.O. Adding the no-arg apply solves this problem while providing the same experience to the caller.
I took the liberty to modify your code with the intention to show how (what I understand of) your problem can be solved using typeclasses and type parameters, instead of type members.
import scala.reflect.runtime.universe.{TypeTag, typeOf}
implicit class Json(val underlying: String) extends AnyVal
implicit class Csv(val underlying: String) extends AnyVal
trait Flavor[W] {
def read[T](wire: W)(implicit tt: TypeTag[T]): T
}
trait Maker[W] {
def make(): Flavor[W]
}
object Maker {
implicit val StringFlavorMaker: Maker[String] = new Maker[String] {
override def make(): Flavor[String] = StringFlavor
}
implicit val JsonFlavorMaker: Maker[Json] = new Maker[Json] {
override def make(): Flavor[Json] = JsonFlavor
}
implicit val CsvFlavorMaker: Maker[Csv] = new Maker[Csv] {
override def make(): Flavor[Csv] = CsvFlavor
}
}
case object StringFlavor extends Flavor[String] {
override final def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
if(tt.tpe =:= typeOf[Int])
0.asInstanceOf[T]
else
throw new Exception("Boom 1")
}
}
case object JsonFlavor extends Flavor[Json] {
override final def read[T](wire: Json)(implicit tt: TypeTag[T]): T = {
if(tt.tpe =:= typeOf[Int])
3.asInstanceOf[T]
else
throw new Exception("Boom 2")
}
}
case object CsvFlavor extends Flavor[Csv] {
override final def read[T](wire: Csv)(implicit tt: TypeTag[T]): T = {
if(tt.tpe =:= typeOf[Int])
5.asInstanceOf[T]
else
throw new Exception("Boom 3")
}
}
object FantasticThing {
def apply[W](implicit maker: Maker[W]): Flavor[W] = maker.make()
}
Then you can create and user any flavour (given there is an implicit maker in scope) this way.
val stringFlavor = FantasticThing[String]
// stringFlavor: Flavor[String] = StringFlavor
stringFlavor.read[Int]("Heavy")
// res0: Int = 0
val jsonFlavor = FantasticThing[Json]
// jsonFlavor: Flavor[Json] = JsonFlavor
jsonFlavor.read[Int]("{'heavy':'true'}")
// res1: Int = 3
val csvFlavor = FantasticThing[Csv]
// csvFlavor: Flavor[Csv] = CsvFlavor
csvFlavor.read[Int]("Hea,vy")
// res2: Int = 0
In general, is better to stay off of type members, since they are more complex and used for more advanced stuff like path dependent types.
Let me know in the comments if you have any doubt.
DISCLAIMER: I am bad with type members (still learning about them), that may motivate me to use different alternatives. - In any case, I hope you can apply something similar to your real problem..
Related
I want to bind a check method to the Test in such a way that the implementation does not contain an argument (look at the last line). It is necessary to use type classes here, but I'm new in Scala, so I have problems.
Object Checker is my attempts to solve the problem. Perhaps it is enough to make changes to it...
trait Test[+T] extends Iterable[T]
class TestIterable[+T](iterable: Iterable[T]) extends Test[T] {
override def iterator: Iterator[T] = iterable.iterator
}
object Test {
def apply[T](iterable: Iterable[T]): Test[T] = new TestIterable[T](iterable)
}
trait Check[M] {
def check(m: M): M
}
object Checker {
def apply[M](implicit instance: Check[M]): Check[M] = instance
implicit def checkSyntax[M: Check](m: M): CheckOps[M] = new CheckOps[M](m)
private implicit def test[T](m: Test[T]) : Check[Test[T]] = {
new Check[Test[T]] {
override def check(m: Test[T]) = m
}
}
final class CheckOps[M: Check](m: M) {
def x2: M = Checker[M].check(m)
}
}
import Checker._
val test123 = Test(Seq(1, 2, 3))
Test(test123).check
Is there a way to return different return types from a single method in scala?
For example, if I have a load() method, I would like to return different data types depending on the object that called this method.
def load(path: String):<return type>
{
// if this instance is of type "type1", do some processing on this object,
// and return object of type "type1"
// else if this instance is of type "type2", do some processing on this object,
// and return object of type return "type2"
}
If I understand correctly what you want, F-bounded polymorphism may work for you:
trait Base[T <: Base[T]] {
def load(path: String): T
}
class Type1 extends Base[Type1] {
override def load(path: String): Type1 = new Type1 // provisional implementation
}
class Type2 extends Base[Type2] {
override def load(path: String): Type2 = new Type2
}
Then load will return the type of the current object. Note the result types in those expressions:
new Type1().load(path)
scala> res2: Type1 = Type1#744f0e0b
new Type2().load(path)
scala> res3: Type2 = Type2#77988c45
You can use scala's Either:
def load(path : String) : Either[Type1, Type2] = {
this match {
case t1 : Type1 => Left(someProcessing(t1))
case t2 : Type2 => Right(someOtherProcessing(t2))
}
}
As others stated, you can use
You can use scala's Either
just keep in mind that every method that invokes your method, will need to check which of the types it is returning (using .map or pattern matching). Either is usually used like Either[ErrorType, NormalType] btw, but of course you can use it however you want
scala cats library has other alternative: http://eed3si9n.com/herding-cats/Xor.html
and of course, scalaz also provides an alternative: http://appliedscala.com/blog/2016/scalaz-disjunctions/
As a last resort, don't you can define your own "Either"
If your requirement is as simple as returning some instance of the type of context instance... ie this then you can just do this,
class A() {
def omg(s: String): this.type = new A()
}
And if inheritence is involved,
trait A {
type omgType
def omg(s: String): omgType
}
class B() extends A {
override type omgType = this.type
override def omg(s: String): omgType = new B()
}
class C() extends A {
override type omgType = this.type
override def omg(s: String): omgType = new C()
}
But if you want more generality then you may want to read the following and apply it there,
The easiest way will be to take inspiration from the magnet pattern which was heavily used in Spray.
We can leverage the inspiration to build our custom solution, remember it is neither pure magnet pattern nor is path dependent type approach. Its a hacky cocktail of both.
So... lets say you want your def process to be able to support input parameters of type Int and String and finally return the respective result.
You will need to define implicit magnets for these types,
trait ProcessMagnet {
type Input
type Result
def input: Input
def process: Result
}
object ProcessMagnetProvider {
implicit def stringToStringProcessMagnet(string: String): ProcessMagnet = new ProcessMagnet {
override type Input = String
override type Result = String
override def input: Input = string
// define this for your doing...
override def process: Result = input + "_omg"
}
//... add for all your inputs
implicit def intToIntProcessMagnet(int: Int): ProcessMagnet = new ProcessMagnet {
override type Input = Int
override type Result = Int
override def input: Input = int
// define this for your doing...
override def process: Result = input + 1
}
}
def process[T](t: T)(implicit pmConverter: T => ProcessMagnet): ProcessMagnet = pmConverter(t)
// now just import our implicit magnets...
import ProcessMagnetProvider._
val intResult: Int = process(5).process.asInstanceOf[Int]
val stringResult: String = process("omg").process.asInstanceOf[String]
what about factory method and just defining a trait loadable eg:
trait Loadable {
def load(path: String): Loadable
}
class Type1 extends Loadable {
def load(path: String): Type1 = this
}
class Type2 extends Loadable {
def load(path: String): Type2 = this
}
object Main {
def test(): Loadable = {
new Type1().load("path")
}
def main(args: Array[String]): Unit = {
println(test().getClass)
}
}
I have written automatic type class derivation in order to automatically generate elasticsearch Json mapping for case classes.
For that I am using the TypeClass type class in shapeless.
The problem I have is that many fields in the case classes we use are Scala enumerations.
For example
object ConnectionState extends Enumeration {
type ConnectionState = Value
val ordering, requested, pending, available, down, deleting, deleted, rejected = Value
}
Or
object ProductCodeType extends Enumeration {
type ProductCodeType = Value
val devpay, marketplace = Value
}
It seems I have to define a specific implicit instance for every Enumeration that is defined in order that the automatic derivation will pick it up (e.g. for both ConnectionState and ProductCodeType).
I cannot have one implicit def for Enumeration such as
implicit def enumerationMapping: MappingEncoder[Enumeration] = new MappingEncoder[Enumeration] {
def toMapping = jSingleObject("type", jString("text"))
}
that will work for all enumeration types.
I tried making the type class covariant, and a bunch of other things but nothing helped.
Any ideas?
Here is the derivation code:
object Mapping {
trait MappingEncoder[T] {
def toMapping: Json
}
object MappingEncoder extends LabelledProductTypeClassCompanion[MappingEncoder] {
implicit val stringMapping: MappingEncoder[String] = new MappingEncoder[String] {
def toMapping = jSingleObject("type", jString("text"))
}
implicit val intMapping: MappingEncoder[Int] = new MappingEncoder[Int] {
def toMapping = jSingleObject("type", jString("integer"))
}
implicit def seqMapping[T: MappingEncoder]: MappingEncoder[Seq[T]] = new MappingEncoder[Seq[T]] {
def toMapping = implicitly[MappingEncoder[T]].toMapping
}
implicit def optionMapping[T: MappingEncoder]: MappingEncoder[Option[T]] = new MappingEncoder[Option[T]] {
def toMapping = implicitly[MappingEncoder[T]].toMapping
}
object typeClass extends LabelledProductTypeClass[MappingEncoder] {
def emptyProduct = new MappingEncoder[HNil] {
def toMapping = jEmptyObject
}
def product[F, T <: HList](name: String, sh: MappingEncoder[F], st: MappingEncoder[T]) = new MappingEncoder[F :: T] {
def toMapping = {
val head = sh.toMapping
val tail = st.toMapping
(name := head) ->: tail
}
}
def project[F, G](instance: => MappingEncoder[G], to: F => G, from: G => F) = new MappingEncoder[F] {
def toMapping = jSingleObject("properties", instance.toMapping)
}
}
}
}
I was able to fix the problem by adding additional implicit defs to scope:
implicit def enumerationMapping[T <: Enumeration#Value]: MappingEncoder[T] = new MappingEncoder[T] {
def toMapping = jSingleObject("type", jString("text"))
}
implicit def enumerationSeqMapping[T <: Enumeration#Value]: MappingEncoder[Seq[T]] = new MappingEncoder[Seq[T]] {
def toMapping = jSingleObject("type", jString("text"))
}
The second implicit was needed as some of the case classes had members of type Seq[T] where T is some enumeration type.
Just when I thought I understood the basics of Scala's type system... :/
I'm trying to implement a class that reads the contents of a file and outputs a set of records. A record might be a single line, but it could also be a block of bytes, or anything. So what I'm after is a structure that allows the type of Reader to imply the type of the Record, which in turn will imply the correct Parser to use.
This structure works as long as MainApp.records(f) only returns one type of Reader. As soon as it can return more, I get this error:
could not find implicit value for parameter parser
I think the problem lies with the typed trait definitions at the top, but I cannot figure out how to fix the issue...
// Core traits
trait Record[T]
trait Reader[T] extends Iterable[Record[T]]
trait Parser[T] {
def parse(r: Record[T]): Option[Int]
}
// Concrete implementations
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] {
val lines = Source.fromFile(f).getLines()
def iterator: Iterator[LineRecord[T]] =
new Iterator[LineRecord[T]] {
def next() = new LineRecord[T]
def hasNext = lines.hasNext
}
}
trait TypeA
object TypeA {
implicit object TypeAParser extends Parser[TypeA] {
def parse(r: Record[TypeA]): Option[Int] = ???
}
}
trait TypeB
object TypeB {
implicit object TypeBParser extends Parser[TypeB] {
def parse(r: Record[TypeB]): Option[Int] = ???
}
}
// The "app"
object MainApp {
def process(f: File) =
records(f) foreach { r => parse(r) }
def records(f: File) = {
if(true)
new FileReader[TypeA](f)
else
new FileReader[TypeB](f)
}
def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
parser.parse(r)
}
First off you must import the implicit object in order to use them:
import TypeA._
import TypeB._
That's not enough though. It seems like you're trying to apply implicits dynamically. That's not possible; they have to be found compile time.
If you import the objects as above and change the records so that the compiler finds the correct generic it will run fine:
def records(f: File) = new FileReader[TypeA](f)
But then it may not be what you were looking for ;)
The problem is that the return type of your records method is basically FileReader[_] (since it can return either FileReader[TypeA] or FileReader[TypeB]), and you don't provide an implicit argument of type Parser[Any]. If you remove the if-expression the return type is inferred to FileReader[TypeA], which works fine. I'm not sure what you're trying to do, but obviously the compiler can't select implicit argument based upon a type that is only known at runtime.
1) Using type with implicit inside as type parameter - doesn't bind this implicit to the host type, to do this change objects to the traits and mix them instead of generalizing (type-parametrizing):
def records(f: File) = {
if(true)
new FileReader(f) with TypeA
else
new FileReader(f) with TypeB
}
2) The parser should be in scope of function that calls parse. So you may try smthg like that:
def process(f: File) = {
val reader = records(f);
import reader._
reader foreach { r => parse(r) }
}
PlanB) Simpler alternative is to define type-parameter specific implicit methods inside the AppMain (or some trait mixed in), but it will work only if TypeA/TypeB is known on compile time, so records method can return concrete type:
implicit class TypeAParser(r: Record[TypeA]) {
def parse: Option[Int] = ???
}
implicit class TypeBParser(r: Record[TypeB]) {
def parse: Option[Int] = ???
}
def process[T <: TypeAorB](f: File) =
records[T](f).foreach(_.parse)
def recordsA[T <: TypeAorB](f: File) = new FileReader[T](f)
Here is, I think, the full set of modifications you need to do to get where I think you want to go.
import scala.io.Source
import java.io.File
import reflect.runtime.universe._
// Core traits
trait Record[+T]
trait Reader[+T] extends Iterable[Record[T]]
trait Parser[-T] {
def parse(r: Record[T]): Option[Int]
}
// Concrete implementations [unmodified]
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] {
val lines = Source.fromFile(f).getLines()
def iterator: Iterator[LineRecord[T]] =
new Iterator[LineRecord[T]] {
def next() = new LineRecord[T]
def hasNext = lines.hasNext
}
}
sealed trait Alternatives
case class TypeA() extends Alternatives
object TypeA {
implicit object TypeAParser extends Parser[TypeA] {
def parse(r: Record[TypeA]): Option[Int] = ???
}
}
case class TypeB() extends Alternatives
object TypeB {
implicit object TypeBParser extends Parser[TypeB] {
def parse(r: Record[TypeB]): Option[Int] = ???
}
}
class ParseAlternator(parserA: Parser[TypeA], parserB: Parser[TypeB]) extends Parser[Alternatives] {
def parse(r: Record[Alternatives]): Option[Int] = r match {
case x: Record[TypeA #unchecked] if typeOf[Alternatives] =:= typeOf[TypeA] => parserA.parse(x)
case x: Record[TypeB #unchecked] if typeOf[Alternatives] =:= typeOf[TypeB] => parserB.parse(x)
}
}
object ParseAlternator {
implicit def parseAlternator(implicit parserA: Parser[TypeA], parserB: Parser[TypeB]): Parser[Alternatives] = new ParseAlternator(parserA, parserB)
}
// The "app"
object MainApp {
import ParseAlternator._
def process(f: File) =
records(f) foreach { r => parse(r) }
def records(f: File): Reader[Alternatives] = {
if(true)
new FileReader[TypeA](f)
else
new FileReader[TypeB](f)
}
def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
parser.parse(r)
}
The gist of it is: all of this would be completely classsical if only your parse instance did not have to pattern-match on a generic type but dealt directly with an Alternative instead.
It's this limitation (inherited from the JVM) that scala can't properly pattern-match on an object of a parametric type that requires the reflection & typeOf usage. Without it, you would just have type alternatives for your content (TypeA, TypeB), which you would add to a sealed trait, and which you would dispatch on, in an implicit that produces a Parser for their supertype.
Of course this isn't the only solution, it's just what I think is the meeting point of what's closest to what you're trying to do, with what's most idiomatic.
Given the code
object Renderer {
sealed abstract class BasicRender
case class RenderImages(img: Array[File]) extends BasicRender
case class RenderVideo(video: File) extends BasicRender
def rendererFor[T <: BasicRender : Manifest, Z <: Render.RenderingContext](ctx: Z): Option[Render[T]] = {
val z = manifest[T].erasure
if (z == classOf[RenderImages]) {
Some(new ImagesRenderer(ctx.asInstanceOf[ImagesContext])) // .asInstanceOf[Render[T]])
} else
if (z == classOf[RenderVideo]) {
Some(new VideoRenderer(ctx.asInstanceOf[VideoContext])) // .asInstanceOf[Render[T]])
} else {
None
}
}
private class ImagesRenderer(ctx: ImagesContext) extends Render[RenderImages] {
override def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = {
None
}
}
private class VideoRenderer(ctx: VideoContext) extends Render[RenderVideo] {
override def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = {
None
}
}
}
trait Render[+Out] {
def renderJSON(json: String)(implicit jsCtx: PhantomJsContext): Option[Out]
}
I made Render trait covariant for it's type parameter, so if
RenderImages <: BasicRender
then
ImagesRenderer <: Render[RenderImages]
But it looks like the compiler is not able to infer the type of renderer in rendererFor, so I need to add explicit class casting like
Some(new ImagesRenderer(ctx.asInstanceOf[ImagesContext]).asInstanceOf[Render[T]])
what is wrong with my reasoning here?
As explained by Daniel C. Sobral, your problem here is that you are instantiating the different renderers dynamically, in a way that does not capture in the type system the relation between ctx and the result type of rendererFor.
One common way to solve this kind of issues is to use a type class:
import java.io.File
class PhantomJsContext
trait Renderer[+Out] {
def renderJSON(json: String)(implicit jsCtx: PhantomJsContext): Option[Out]
}
trait RendererFactory[ContextType, ResultType] {
def buildRenderer( ctx: ContextType ): Renderer[ResultType]
}
object Renderer {
case class RenderImages(img: Array[File])
case class RenderVideo(video: File)
trait ImagesContext
trait VideoContext
def rendererFor[ContextType, ResultType](ctx: ContextType)( implicit factory: RendererFactory[ContextType, ResultType] ): Renderer[ResultType] = {
factory.buildRenderer( ctx )
}
class ImagesRenderer(ctx: ImagesContext) extends Renderer[RenderImages] {
def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = ???
}
implicit object ImagesRendererFactory extends RendererFactory[ImagesContext, RenderImages] {
def buildRenderer( ctx: ImagesContext ) = new ImagesRenderer( ctx )
}
class VideoRenderer(ctx: VideoContext) extends Renderer[RenderVideo] {
def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = ???
}
implicit object VideoRendererFactory extends RendererFactory[VideoContext, RenderVideo] {
def buildRenderer( ctx: VideoContext ) = new VideoRenderer( ctx )
}
}
You can easily check in the REPL that the correct types are returned:
scala> lazy val r1 = Renderer.rendererFor( new Renderer.ImagesContext {} )
r1: Renderer[Renderer.RenderImages] = <lazy>
scala> :type r1
Renderer[Renderer.RenderImages]
scala> lazy val r2 = Renderer.rendererFor( new Renderer.VideoContext {} )
r2: Renderer[Renderer.RenderVideo] = <lazy>
scala> :type r2
Renderer[Renderer.RenderVideo]
T is not to be inferred: it is a parameter that is passed to the method, and there isn't really any guarantee that what is being returned is an Option[Render[T]]. For instance, assume you pass RenderImages and it returns a VideoRenderer, then it would obviously be wrong.
Now, the if conditionals you put in there might prevent that from happening, but none of that is used by the compiler to figure out whether you are returning the right type or not.