【问题标题】:Scala implicit conversion of container nested types容器嵌套类型的 Scala 隐式转换
【发布时间】:2017-07-07 12:37:12
【问题描述】:

考虑以下示例:

case class A()

case class B()

object Conversions {
  implicit def aToB(a: A): B = B()

  implicit def convert[U, T](seq: Seq[U])(implicit converter: U => T): Seq[T] = {
    seq.map(converter)
  }
}

object Main {
  import Conversions._

  def main(args: Array[String]): Unit = {

    val sa = Seq(A())

    def example(): Seq[B] = sa
  }
}

这个例子不会被 scala 编译器编译为 2.11.8 版本。我使用 IntelliJ Idea 进行编译,但实际上想法不会即时产生错误并显示隐式用于转换: Screenshot from Intellij Idea
为了解决这个问题,我使用了这里描述的方法: "Scala: Making implicit conversion A->B work for Option[A] -> Option[B]"
我的代码开始如下所示:

case class A()

case class B()

object Conversions {
  implicit def aToB(a: A): B = B()

  trait ContainerFunctor[Container[_]] {
    def map[A, B](container: Container[A], f: A => B): Container[B]
  }

  implicit object SeqFunctor extends ContainerFunctor[Seq] {
    override def map[A, B](container: Seq[A], f: (A) => B): Seq[B] = {
      Option(container).map(_.map(f)).getOrElse(Seq.empty[B])
    }
  }

  implicit def functorConvert[F[_], A, B](x: F[A])(implicit f: A => B, functor: ContainerFunctor[F]): F[B] = functor.map(x, f)
}

object Main {

  import Conversions._

  def main(args: Array[String]): Unit = {

    val sa = Seq(A())

    def example(): Seq[B] = sa
  }
}

此代码编译良好,可按需运行。

我的问题是:
为什么第一种方法编译失败?
这是否与类型擦除有关,如果是,Functor 的使用如何帮助它?
编译器如何解决这两种情况的隐含?

【问题讨论】:

    标签: scala implicit-conversion functor implicit scala-compiler


    【解决方案1】:

    为什么第一种方法编译失败?

    I've opened a bug for this issue.

    这似乎是隐式搜索中的编译器怪癖。由于您提供了转换Seq[A] => Seq[B]convert 方法,因此编译器无法正确对齐类型。这是用Ytyper-debug编译的输出:

    |    [search #3] start `[U, T](seq: Seq[U])(implicit converter: U => T)Seq[T]` inferring type T, searching for adaptation to pt=A => T (silent: method example in Test) implicits disabled
    |    [search #3] considering aToB
    |    |-- { ((a: A) => Conversions.aToB(a)) } : pt=A => ? EXPRmode (silent: method example in Test) implicits disabled
    |    |    |-- ((a: A) => Conversions.aToB(a)) : pt=A => ? EXPRmode (silent: method example in Test) implicits disabled
    |    |    |    |-- Conversions.aToB(a) EXPRmode (silent: value $anonfun in Test) implicits disabled
    |    |    |    |    |-- Conversions.aToB BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value $anonfun in Test) implicits disabled
    |    |    |    |    |    \-> (a: A)B
    |    |    |    |    |-- a : pt=A BYVALmode-EXPRmode (silent: value $anonfun in Test) implicits disabled
    |    |    |    |    |    \-> A
    |    |    |    |    \-> B
    |    |    |    \-> A => B
    |    |    \-> A => B
    |    [adapt] aToB adapted to { ((a: A) => Conversions.aToB(a)) } based on pt A => T
    |    [search #3] solve tvars=?T, tvars.constr= >: B
    |    solving for (T: ?T)
    |    [search #3] success inferred value of type A => =?B is SearchResult({
    |      ((a: A) => Conversions.aToB(a))
    |    }, TreeTypeSubstituter(List(type T),List(B)))
    |    solving for (A: ?A)
    |    solving for (A: ?A)
    |    solving for (A: ?A)
    |    solving for (A: ?A)
    |    [search #3] considering $conforms
    |    solving for (A: ?A)
    |    [adapt] $conforms adapted to [A]=> <:<[A,A] based on pt A => T
    |    [search #3] solve tvars=?T, tvars.constr= >: A
    |    solving for (T: ?T)
    |    [search #3] success inferred value of type A => =?A is SearchResult(scala.Predef.$conforms[A], TreeTypeSubstituter(List(type T),List(A)))
    

    似乎搜索#3 正在尝试调整conforms (&lt;:&lt;),它将整个隐式搜索从A =&gt; B 带到A =&gt; A。如果我使用-Yno-predef 编译,则隐式转换成功:

    |    |    |-- [U, T](seq: Seq[U])(implicit converter: U => T)Seq[T] : pt=Seq[B] EXPRmode (silent: method example in Test) implicits disabled
    |    |    |    [search #4] start `[U, T](seq: Seq[U])(implicit converter: U => T)Seq[T]`, searching for adaptation to pt=A => B (silent: method example in Test) implicits disabled
    |    |    |    [search #4] considering aToB
    |    |    |    |-- { ((a: A) => Conversions.aToB(a)) } : pt=A => B EXPRmode (silent: method example in Test) implicits disabled
    |    |    |    |    |-- ((a: A) => Conversions.aToB(a)) : pt=A => B EXPRmode (silent: method example in Test) implicits disabled
    |    |    |    |    |    |-- Conversions.aToB(a) : pt=B EXPRmode (silent: value $anonfun in Test) implicits disabled
    |    |    |    |    |    |    |-- Conversions.aToB BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value $anonfun in Test) implicits disabled
    |    |    |    |    |    |    |    \-> (a: A)B
    |    |    |    |    |    |    |-- a : pt=A BYVALmode-EXPRmode (silent: value $anonfun in Test) implicits disabled
    |    |    |    |    |    |    |    \-> A
    |    |    |    |    |    |    \-> B
    |    |    |    |    |    \-> A => B
    |    |    |    |    \-> A => B
    |    |    |    [adapt] aToB adapted to { ((a: A) => Conversions.aToB(a)) } based on pt A => B
    |    |    |    [search #4] success inferred value of type A => B is SearchResult({
    |    |    |      ((a: A) => Conversions.aToB(a))
    |    |    |    }, )
    |    |    |    |-- [U, T](seq: Seq[U])(implicit converter: U => T)Seq[T] : pt=Seq[B] EXPRmode (silent: method example in Test) implicits disabled
    |    |    |    |    \-> Seq[B]
    |    |    |    [adapt] [U, T](seq: Seq[U])(implicit converter: U => T)Seq[T] adapted to [U, T](seq: Seq[U])(implicit converter: U => T)Seq[T] based on pt Seq[B]
    |    |    |    \-> Seq[B]
    |    |    [adapt] Seq[A] adapted to [U, T](seq: Seq[U])(implicit converter: U => T)Seq[T] based on pt Seq[B]
    |    |    \-> Seq[B]
    |    \-> [def example] ()Seq[B]
    

    这是否与类型擦除有关,如果是,如何使用 Functor 有帮助吗?

    第二个示例有效,因为您现在明确说明如何使用Functor 类型类将Seq[A] 映射到Seq[B],因此当编译器看到Seq[A] 时,它具有隐式将其转换为Seq[B]:

    def example(): Seq[B] = Conversions.functorConvert[Seq, A, B](sa)({
            ((a: A) => Conversions.aToB(a))
    }, Conversions.SeqFunctor);
    

    请注意,您需要从A =&gt; B 进行转换,并且需要Functor[Seq] 才能映射所有As 以将它们转换为Bs,这就是它使用conversions.aToB 所做的事情。

    【讨论】:

    • 感谢您的回复。完美的解释。
    • @andrii.ilin 我的解释实际上是有缺陷的,因为我错过了在您的第一个示例中转换Seq[A] =&gt; Seq[B]convert 方法。现在深入研究它。
    • 我认为您的第一个解释涵盖了这一点。仍然感谢您的解释。
    • @andrii.ilin 查看我的新解释。我认为这是一个错误。使用Yno-predef 编译实际上可以完成这项工作。
    • @andrii.ilin 和隐式搜索是一样的,我只是用特殊的标志来查看隐式搜索的过程。基本上,由于某种原因,编译器会尝试将A =&gt; B 转换为A =&gt; APredef.conforms,这样就不会为convert 留下任何变化,以找到合适的隐式。一旦人们更新问题,我将更新此答案。
    猜你喜欢
    • 2018-01-13
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    • 2011-09-12
    • 1970-01-01
    • 2011-06-08
    • 2013-02-23
    • 2018-03-15
    相关资源
    最近更新 更多