The title might be quite vague, but here is the code: https://github.com/amorfis/why-no-implicit
So there is a tool to transform Map[String, Any] to a simple case class. The tests pass and this piece of code illustrates what it is all about:
case class TargetData(
groupId: String,
validForAnalysis: Boolean,
applicationId: Int
)
val map = Map(
"groupId" -> "123456712345",
"applicationId" -> 31,
"validForAnalysis" -> true
)
val transformed: TargetData = MapDecoder.to[TargetData](map).transform
This code works. It nicely creates the case class instance when provided the simple map
However, the transform method has to be called "outside" - just like in the example. When I try to move it to the MapDecoder.to method - the compiler complains about the missing implicit.
So I change the code in MapDecoder.to from this:
def to[A](map: Map[String, Any]) = new MapDecoderH[A](map)
to this:
def to[A](map: Map[String, Any]) = new MapDecoderH[A](map).transform
and it stops working. Why is that? Why the implicit is provided in one case but not in the other? All that changes is that I want to call the transform method in other place to have MapDecoder.to returning the case class not some transformer.
UPDATE:
What if I want to implement to[A] method inside an object I want to transform? Let's call it DataFrame, and I want this code to work:
val df: DataFrame = ...
df.to[TargetData] // There is no apply called here
The problem is in such case there is nothing to pass to apply. It is also not feasible to call it with parens (df.to[TargetData]()) because then the compiler requires implicits in parens. Is it even possible to solve it without using macros?
Implicit can be provided when the compiler can unambiguously find a value in the current scope with matching type.
Outside
def tocompiler sees that you wantMapDecoder[TargetData].Inside it sees
MapDecoder[A]and have no reason to believe thatA =:= TargetData.In such situation you'd have to pass all the implicits as arguments of
tomethod. From your code it seems it would have to be something likebut it would break the ergonomy, since you'd have to add additional parameter which should be inferred but cannot - in Scala 2 you are passing all type arguments explicitly or none. There are ways to work around it like by splitting the type param application into 2 calls like this:
which would be used as
desugared by compiler to
It would be very similar to
MapDecoder.to[TargetData](map).transformbut through a trick it would look much nicer.