Reputation: 736
I was trying to workout the classic example of converting arbitrary values into their Json representation and having compile time errors in case conversion is not defined.
So far, I have,
trait Json
trait ConvertableToJson[A] {
def toJson: Json
}
object Json {
case class Str(str: String) extends Json
case class Int(int : Int) extends Json
case class StrToJson(s: String) extends ConvertableToJson[StrToJson] {
override def toJson: Json = Str(s)
}
}
implicit def str2Json(s: String): StrToJson = StrToJson(s)
def toJson[A <: ConvertableToJson[A]](a: A) = a.toJson
println(toJson("some string"))
I expected the above code to work like:
toJson("some string")
to fail to compile without the implicit def
. Because String <: ConvertableToJson[String]
is false.
But then to use, the implicit def
and find Str2Json
.
Str2Json <: ConvertableToJson[Str2Json]
should be true.
However, this doesn't happen and compiler complains:
Error: Inferred type arguments [String] do not conform to method toJson's type parameter bounds [A <: scalaz.ConvertToJson.ConvertableToJson[A]]
println(toJson("dhruv"))
^
It'll be great if someone can help me correct my understanding
Upvotes: 1
Views: 348
Reputation: 10928
So there are two problems with your code. First of all String
does not extend ConvertableToJson[String]
, which is what your last function call is trying to do.
Second case class StrToJson
should extend ConvertableToJson[String]
not ConvertableToJson[StrToJson]
.
Then your code be made to compile by using view-bounds <%
(see the working example below). This however is a bad idea because view-bounds are being deprecated as a language feature, you should use type classes instead.
trait Json
trait ConvertableToJson[A] {
def toJson: Json
}
object Json {
case class Str(str: String) extends Json
case class Int(int : Int) extends Json
case class StrToJson(s: String) extends ConvertableToJson[String] {
override def toJson: Json = Str(s)
}
}
import Json._
implicit def str2Json(s: String): StrToJson = StrToJson(s)
def toJson[A <% ConvertableToJson[A]](a: A) = a.toJson
println(toJson("some string"))
Using typeclasses
trait Json
trait ConvertableToJson[A] {
// NOTE: this now takes a parameter
def toJson(a: A): Json
}
object Json {
case class Str(str: String) extends Json
case class Int(int : Int) extends Json
}
import Json._
// NOTE: Because toJson takes a parameter the implicit implementation can now be an object
implicit object Str2Json extends ConvertableToJson[String] {
override def toJson(a: String): Json = Str(a)
}
// NOTE: If you want to support the a.toJson syntax this implicit class adds it for all types with an implicit ConvertableToJson
implicit class ConvertableToJsonSyntax[A](a: A)(implicit ev: ConvertableToJson[A]) {
def toJson: Json = ev.toJson(a)
}
// NOTE: Now we use context-bounds instead of view-bounds
def toJson[A : ConvertableToJson](a: A) = a.toJson
// NOTE: we can expand the context-bounds
def toJson2[A](a: A)(implicit ev: ConvertableToJson[A]) = a.toJson
// NOTE: But since we have the type class instance now, we do not need the extra syntax
def toJson3[A](a: A)(implicit ev: ConvertableToJson[A]) = ev.toJson(a)
println(toJson("some string"))
Upvotes: 2