Scala隐式转换容器嵌套类型

时间:2017-07-07 12:37:12

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

考虑以下示例:

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的用法如何帮助它? 编译器如何解决这两种情况的含义?

1 个答案:

答案 0 :(得分:4)

  

为什么第一种方法无法编译?

I've opened a bug for this issue

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

Ytyper-debug

似乎搜索#3正在尝试调整| [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))) conforms),它将整个隐式搜索从<:<转移到A => B。如果我使用A => A进行编译,则隐式转换成功:

-Yno-predef
  

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

第二个示例有效,因为您现在明确规定如何使用| | |-- [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] 类型类将Seq[A]映射到Seq[B],从而在编译器看到时Functor,隐含将其转换为Seq[A]

Seq[B]

请注意,您需要转化def example(): Seq[B] = Conversions.functorConvert[Seq, A, B](sa)({ ((a: A) => Conversions.aToB(a)) }, Conversions.SeqFunctor); A => B才能映射所有Functor[Seq]以将其转换为A s,这就是它使用B