为什么Scala编译器可以提供隐式的out of object,但不能提供inside?

amo*_*fis 1 scala implicit shapeless

标题可能很模糊,但这是代码: https: //github.com/amorfis/why-no-implicit

所以有一个工具可以转化Map[String, Any]为简单的案例类。测试通过了,这段代码说明了它的全部内容:

        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
Run Code Online (Sandbox Code Playgroud)

这段代码有效。当提供简单的情况时,它很好地创建了案例类实例map

但是,该transform方法必须在“外部”调用 - 就像示例中一样。当我尝试将其移至该MapDecoder.to方法时 - 编译器抱怨缺少隐式。

所以我将代码更改MapDecoder.to为:

def to[A](map: Map[String, Any]) = new MapDecoderH[A](map)
Run Code Online (Sandbox Code Playgroud)

对此:

def to[A](map: Map[String, Any]) = new MapDecoderH[A](map).transform
Run Code Online (Sandbox Code Playgroud)

它停止工作了。这是为什么?为什么在一种情况下提供了隐含的内容,而在另一种情况下则没有提供?所有的变化是我想transform在其他地方调用该方法以MapDecoder.to返回案例类而不是某个转换器。

更新:

to[A]如果我想在要转换的对象内实现方法怎么办?让我们称之为DataFrame,我希望这段代码能够工作:

val df: DataFrame = ...
df.to[TargetData] // There is no apply called here
Run Code Online (Sandbox Code Playgroud)

问题是在这种情况下没有什么可以传递给apply。用括号 ( ) 调用它也是不可行的,df.to[TargetData]()因为编译器需要在括号中隐含。是否可以不使用宏来解决它?

Mat*_*zok 5

当编译器可以明确地在当前范围内找到具有匹配类型的值时,可以提供隐式。

外部def to编译器看到你想要的MapDecoder[TargetData]

它所看到的MapDecoder[A]并没有理由相信A =:= TargetData

在这种情况下,您必须将所有隐式作为to方法的参数传递。从你的代码看来它必须是类似的

def to[A, R <: HList](map: Map[String, Any])(implicit
  gen: LabelledGeneric.Aux[A, R],
  transformer: MapDecoder[R]
) = new MapDecoderH[A](map).transform
Run Code Online (Sandbox Code Playgroud)

但这会破坏人体工程学,因为你必须添加额外的参数,这些参数应该被推断,但不能 - 在 Scala 2 中,你要么显式传递所有类型参数,要么不传递任何类型参数。有多种方法可以解决这个问题,例如将类型参数应用程序拆分为 2 个调用,如下所示:

class Applier[A] {

  def apply[R <: HList](map: Map[String, Any])(implicit
    gen: LabelledGeneric.Aux[A, R],
    transformer: MapDecoder[R]
  ) = new MapDecoderH[A](map).transform
}

def to[A] = new Applier[A]
Run Code Online (Sandbox Code Playgroud)

这将被用作

MapDecoder.to[A](map)
Run Code Online (Sandbox Code Playgroud)

由编译器脱糖为

MapDecoder.to[A].apply[InferredR](map)(/*implicit*/gen, /*implicit*/transformer)
Run Code Online (Sandbox Code Playgroud)

它会非常相似, MapDecoder.to[TargetData](map).transform但通过一个技巧,它看起来会更好。