Consider this code:
sealed trait Data
case class StringData(string: String) extends Data
case class IntData(int: Int) extends Data
trait Reader[A] {
def read(data: Data): A
}
implicit val stringReader: Reader[String] = {
case StringData(string) => string
case _ => sys.error("not a string")
}
implicit val intReader: Reader[Int] = {
case IntData(int) => int
case _ => sys.error("not an int")
}
With this in scope, I want to write an implicit method that silently converts from Data values to their "real" Scala values.
implicit def fromData[A: Reader](data: Data): A =
implicitly[Reader[A]].read(data)
But then, this code does not compile:
val str: String = StringData("foo")
val int: Int = IntData(420)
The error is a type mismatch. Standard debugging methods for implicits show that the A from fromData can't be infered (all implicit Readers are shown as applicable).
For your convenience, this is a link to a scastie of the code. In this other scastie, a similiar, yet different, and working snippet is presented.
My question: What is going on here?
Making the change to your Data class as well as your reader implicit conversion as below allows the code to compile.
import scala.language.implicitConversions
sealed trait Data[A]
case class StringData(string: String) extends Data[String]
case class IntData(int: Int) extends Data[Int]
trait Reader[A] {
def read(data: Data[A]): A
}
implicit val stringReader: Reader[String] = {
case StringData(string) => string
case _ => sys.error("not a string")
}
implicit val intReader: Reader[Int] = {
case IntData(int) => int
case _ => sys.error("not an int")
}
implicit def fromData[A](data: Data[A])(implicit ev: Reader[A]): A = ev.read(data)
val str: String = StringData("foo")
val int: Int = IntData(420)
You need data to be typed so that the compiler can infer which Reader should be used based on the type parameter of Data. Also notice that the following would not compile if you did not define an implicit Reader.
case class DoubleData(d: Double) extends Data[Double]
// Fails compilation because no implicit Reader exists.
val d: Double = DoubleData(5d)
Related
I don't get the following code to compile and I am curious of what I did wrong.
I defined a Contravariant Jsonwriter Trait and a function accepting implicit writers:
trait JsonWriter[-A] {
def write(value: A): Json
}
object Json {
def toJson[A](value: A)(implicit writer: JsonWriter[A]): Json =
writer.write(value)
}
Additionally I have defined some instances of these writers:
object JsonWriterInstances {
implicit val stringWriter: JsonWriter[String] =
(value: String) => JsString(value)
implicit val doubleWriter: JsonWriter[Double] =
(value: Double) => JsNumber(value)
class OptionWriter[-T](writer: JsonWriter[T]) extends JsonWriter[Option[T]] {
def write(value: Option[T]): Json = {
value match {
case None => JsNull
case Some(x) => writer.write(x)
}
}
}
implicit def optionWriter[T](implicit writer: JsonWriter[T]):
JsonWriter[Option[T]] = new OptionWriter[T](writer)
}
Now I have written a test:
"write double Option" in {
Some(1.0).toJson should be(JsNumber(1.0))
None.toJson should be(JsNull)
}
The first test for Some(1.0) works fine
The second one for None throws:
Error:(40, 12) could not find implicit value for parameter writer: JsonWriter[None.type]
None.toJson should be(JsNull)
If you want to try the code my JsonType definitions for this example are:
sealed trait Json
final case class JsObject(get: Map[String, Json]) extends Json
final case class JsString(get: String) extends Json
final case class JsNumber(get: Double) extends Json
case object JsNull extends Json
None, if you dont say anything else, is a Option[Nothing], so OptionWriter[Nothing] needs a JsonWriter[Nothing]. If you try Json.toJson(Some(1)) is the same, there is no JsonWriter[Int].
On the other hand, Json.toJson(None:Option[String]) works, because OptionWriter[String] can get a JsonWriter[String].
I think it has to do with the fact that
case object None extends Option[Nothing] { ... }
if you do one of the following, it will work
toJson(Option.empty[Double])
toJson(None : Option[Double])
Note that the second one uses type ascription to put a face, so to speak, on the Nothing (which is a subtype of everything)
I very roughly have the following code:
object MyObj {
def callWithParams(params: List[Param]): String = "some string"
}
sealed trait Param
case class Single(id: Int) extends Param
case class Group(id: Int, subParams: List[Param]) extends Param
def buildMyParams(): List[Param] = List(Single(1), Group(2, List(Group(3, Single(4))))
def macroImpl(c: blackbox.Context): c.Expr[String] = {
import c.universe._
// TODO: need a implicit Lift[Param] implementation here
val myParams = buildMyParams()
c.Expr[String](q"MyObj.callWithParams($myParams)")
}
My attempt at the implicit List[Param] is:
implicit val lift = Liftable[Param]({
case s: Single => q"Single(${s.id})"
case g: Group => q"Group(${g.id}, ${g.subParams})"
})
This doesn't compile because of the forward reference in the Liftable when trying to lift a Group, because of Group.subParams.
How do I get around this issue?
Declaring the lift as a def rather than a val was enough to fix this:
implicit def lift = Liftable[Param]({
case s: Single => q"Single(${s.id})"
case g: Group => q"Group(${g.id}, ${g.subParams})"
})
I need to check integrity of nested schemas and hence am writing case classes to do so. The main hurdle I am facing is the schema may have a field (say, name) either of a String or a Utf8 type and I want to accept both the instances. Is it possible to avoid having two case classes as
case class NameValueString(name: String, value: Double)
case class NameValueUtf8(name: Utf8, value: Double)
and something like
case class NameValue(name #(_:String | _:Utf8), value: Double)
The above expression certainly fails compilation.
Nikhil
One approach is so-called type classes:
trait StringLike[A] // sealed if you don't want anybody to implement it elsewhere
object StringLike {
implicit object StringEv extends StringLike[String] {}
implicit object Utf8Ev extends StringLike[Utf8] {}
}
case class NameValue[A](name: A, value: Double)(implicit val stringLike: StringLike[A])
Of course, StringLike will normally not be empty, but describe whatever common functionality you need from both String and Utf8.
You can match on the evidence:
def nameLength[A](nameValue: NameValue[A]) = nameValue.stringLike match {
case StringLike.StringEv =>
nameValue.name.length // calls String#length
case StringLike.Utf8Ev =>
nameValue.name.length // calls Utf8#length (assuming Utf8 has such method)
}
In this case the compiler will even know that A (and so the type of nameValue.name) is String in the first branch and Utf8 in the second.
Another pattern (doesn't require implicit arguments):
import scala.language.implicitConversions
class StringLike[A](name: A) {
override def toString = {
name match {
case s: String => s"String: $s"
case i: Int => s"Int: $i"
}
}
}
implicit def string2StringLike(s: String) = new StringLike(s)
implicit def int2StringLike(i: Int) = new StringLike(i)
case class NameValue[A](name: StringLike[A], value: String) {
override def toString = name.toString
}
NameValue("123", "123")
//> NameValue[String] = String: 123
NameValue(13, "123")
//> NameValue[Int] = Int: 13
NameValue(13.9, "123")
// error: type mismatch;
// found : Double(13.9)
// required: StringLike[?]
// NameValue(13.9, "123")
// ^
UPDATE
Here's how I see completed type class approach based on Alexey's answer:
trait StringLike[A] {
def toString(x: A): String
}
object StringLike {
implicit object StringStringLike extends StringLike[String] {
def toString(s: String) = s"String: $s"
}
implicit object IntStringLike extends StringLike[Int] {
def toString(i: Int) = s"Int: $i"
}
}
import StringLike._
case class NameValue[A](name: A, value: Double)(implicit ev: StringLike[A]) {
override def toString = ev.toString(name)
}
NameValue(1, 2.0)
//> NameValue[Int] = Int: 1
NameValue("123", 2.0)
//> NameValue[String] = String: 123
NameValue(2.0, 2.0)
// error: could not find implicit value for parameter ev:
// StringLike[Double]
// NameValue(2.0, 2.0)
// ^
UPDATE2
One more (using union type for type safety):
type ¬[A] = A => Nothing
type ¬¬[A] = ¬[¬[A]]
type ∨[T, U] = ¬[¬[T] with ¬[U]]
type |∨|[T, U] = { type λ[X] = ¬¬[X] <:< (T ∨ U) }
def nameLength[A: ClassTag: (Int |∨| String)#λ](nameValue: NameValue[A]) =
nameValue.name match {
case s:String => s.length
case i:Int => i + 1
}
As you are using case class already, if you just need different ways to create it, and you are ok on keeping your data represented in only one way, you can add your own apply method to enable the creation using different parameters.
case class NameValue(name: String, value: Double)
object NameValue{
def apply(name: Utf8, value: Double): NameValue = {
new NameValue( name.toString, value )
}
}
Alternatively, if you would like to pattern match and extract NameValue from different options, you may need to check Extractors, which is basically create your own unapply methods... check http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html
Let's say someone provided a function:
def getTupleData[T](source: String): List[T] = {
// ...
}
I need to write a function which takes a case class C as the type parameter and return List[C] with the help of the above function. Here is what I have got so far:
def getCaseClassData[C](source: String): List[C] = {
// Somehow get T from C.
// For example, if C is case class MyCaseClass(a: Int, b: Long), then T is (Int, Long)
// How to get T?
getTupleData[T](source) map { tuple: T =>
// Somehow convert tuple into a case class instance with the case class type parameter
// C.tupled(tuple) ?? Type parameter cannot be used like this. :(
}
}
More specifically, it seems to me I'm asking two questions here:
How to explicitly obtain the type of the tuple from a type parameter which represents a case class so that it can be used as a type parameter?
How to create a case class instance from a tuple instance without knowing the actual name of the case class but only a type parameter?
You won't find any reasonably simple or direct way to do it. If you' re ready for the more involved solutions, bear with me.
Every case class has an apply method in its companion object, which instantiates the class. By calling tupled on this method (after eta-expansion), you'll get a function that takes a tuple and creates the corresponding case class instance.
Now of course the problem is that the every case class's apply has a different signature. We can get around this by introducing a type class representing a case class factory, and provide instances of this type class through a macro (which will just delegate to the case class's apply method).
import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
trait CaseClassFactory[C,T]{
type Class = C
type Tuple = T
def apply(t: Tuple): C
}
object CaseClassFactory {
implicit def factory1[C,T]: CaseClassFactory[C,T] = macro factoryImpl[C,T]
implicit def factory2[C]: CaseClassFactory[C,_] = macro factoryImpl[C,Nothing]
def apply[C,T]: CaseClassFactory[C,T] = macro factoryImpl[C,T]
def apply[C]: CaseClassFactory[C,_] = macro factoryImpl[C,Nothing]
def factoryImpl[C:c.WeakTypeTag,T:c.WeakTypeTag](c: Context) = {
import c.universe._
val C = weakTypeOf[C]
val companion = C.typeSymbol.companion match {
case NoSymbol => c.abort(c.enclosingPosition, s"Instance of $C has no companion object")
case sym => sym
}
val tupledTree = c.typecheck(q"""($companion.apply _).tupled""")
val T = tupledTree.tpe match {
case TypeRef(_, _, List(argTpe, _)) => argTpe
case t => c.abort(c.enclosingPosition, s"Expecting type constructor (Function1) for $C.tupled, but got $t: ${t.getClass}, ${t.getClass.getInterfaces.mkString(",")}")
}
if (! (c.weakTypeOf[T] <:< T)) {
c.abort(c.enclosingPosition, s"Incompatible tuple type ${c.weakTypeOf[T]}: not a sub type of $T")
}
q"""
new CaseClassFactory[$C,$T] {
private[this] val tupled = ($companion.apply _).tupled
def apply(t: Tuple): $C = tupled(t)
}
"""
}
}
With it you can do something like this:
scala> case class Person(name: String, age: Long)
defined class Person
scala> val f = CaseClassFactory[Person]
f: CaseClassFactory[Person]{type Tuple = (String, Long)} = $anon$1#63adb42c
scala> val x: f.Tuple = ("aze", 123)
x: f.Tuple = (aze,123)
scala> implicitly[f.Tuple =:= (String, Long)]
res3: =:=[f.Tuple,(String, Long)] = <function1>
scala> f(("aze", 123))
res4: Person = Person(aze,123)
But more importantly, you can require an instance of CaseClassFactory as an implicit parameter, allowing to generically instantiate your case classes. You can then do something like:
scala> implicit class TupleToCaseClassOps[T](val t: T) extends AnyVal {
| def toCaseClass[C](implicit f: CaseClassFactory[C,T]): C = {
| f(t)
| }
| }
defined class TupleToCaseClassOps
scala> case class Person(name: String, age: Long)
defined class Person
scala> ("john", 21).toCaseClass[Person]
res5: Person = Person(john,21)
Pretty neat. Armed with this type class, getCaseClassData then becomes:
def getCaseClassData[C](source: String)(implicit f: CaseClassFactory[C,_]): List[C] = {
getTupleData[f.Tuple](source) map { tuple: f.Tuple =>
f(tuple)
}
}
I want to return a type from a function. For example:
class Super
case class One(a: Int) extends Super
case class Two(b: Float) extends Super
case class Unknown extends Super
def decide(criterion: String): ??? = {
criterion match {
case "one" => One
case "two" => Two
case _ => Unknown
}
}
So I want to return the type itself, to store it in a Map so that I could apply it somewhere later:
val test = Buffer(
("ahaha" -> "one")
("ohoho" -> "two")
("lalala" -> "one")
)
var map = scala.collection.mutable.Map[String, Super]()
test.map {pair =>
map(pair._1) = decide(pair._2)
}
So that later I could like:
def act(code: String) {
map(code) match {
case One => doSmth[One]()
case Two => doSmth[Two]()
case _ => doNothing()
}
}
I know that some parts, like the unused parameters of the case classes may seem strange here, but this is how it is in the environment I am working in, and this example is that full because I am not sure if it will differ if I take something away...
So how can I make the decide function return a type and then use it in a manner similar to what I have shown?
I think you may want case object One, etc, rather than using Class or ClassTag. Then you get useful match support. For the act method, your case objects could return a ClassTag or similar, or just let act associate One with doSmth[OneClass] etc.
It seems you can make your case companions into case objects. Isn't that special.
package typeswitch
import reflect.runtime.universe._
sealed trait Selection
class Super
case class One(a: Int) extends Super
case object One extends Selection
case class Two(b: Float) extends Super
case object Two extends Selection
case class Unknown() extends Super
case object Unknown extends Selection
object Test extends App {
type What = Selection
def decide(criterion: String): What = criterion match {
case "one" => One
case "two" => Two
case _ => Unknown
}
val test = List(
"ahaha" -> "one",
"ohoho" -> "two",
"lalala" -> "one"
)
val m = scala.collection.mutable.Map[String, What]()
test map (pair => m(pair._1) = decide(pair._2))
def act(code: String) = m(code) match {
case One => doSmth[One]()
// non-exhaustive
//case Two => doSmth[Two]()
case Unknown => doNothing()
// handle exhaustively
case s: Selection => doSmthNew(s)
}
def doSmthElse[A <: Super]()(implicit t: TypeTag[A]): A = {
Console println s"Do st with $t"
val claas: Class[_] = t.mirror.runtimeClass(t.tpe)
null.asInstanceOf[A]
}
def doSmth[A <: Super]()(implicit t: ClassTag[A]): A = {
Console println s"Do st with $t"
val claas: Class[_] = t.runtimeClass
null.asInstanceOf[A]
}
def doSmthNew[A >: What : ClassTag, B <: Super](what: A): B = {
Console println s"Do st new with $what"
null.asInstanceOf[B]
}
def doNothing() { }
val res = act("lalala")
Console println s"Got $res?"
}
Sorry if this is too basic, but:
$ scala
Welcome to Scala version 2.10.0-RC2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_06).
Type in expressions to have them evaluated.
Type :help for more information.
scala> trait Bar
defined trait Bar
scala> case class Foo(i:Int) extends Bar
defined class Foo
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> def f[A <: Bar : TypeTag]() = println(s" Do ${ implicitly[TypeTag[A]] }")
f: [A <: Bar]()(implicit evidence$1: reflect.runtime.universe.TypeTag[A])Unit
scala> f[Foo]
Do TypeTag[Foo]