(co | in)更高的Kinded类型参数的方差

时间:2014-06-04 23:03:16

标签: scala types covariance

有问题的代码

trait Functor[F[_]] {
  def map[A, B](f: A => B): F[A] => F[B]
}

sealed abstract class Free[F[_], A]
case class Return[F[_], A](x: A) extends Free[F, A]
case class Suspend[F[_], A](x: F[Free[F, A]]) extends Free[F, A]
case class Bind[F[_], A, B](x: () => Free[F, A], f: A => Free[F, B]) extends Free[F, B]

// this is the problem child
def liftF[F[_], A](x: => F[A])(implicit F: Functor[F]) = 
  Suspend[F, A](F.map { Return[F, A] }(x))

现在由于某种原因,在eclipse scala ide中我遇到了一个与liftF有关的错误   这个错误

- type mismatch; found : F[core01.Free.Return[F,A]] required: F[core01.Free[F,A]] Note: core01.Free.Return[F,A] <: core01.Free[F,A], but type 
 F is invariant in type _. You may wish to define _ as +_ instead. (SLS 4.5)

现在在scalaz源代码中没有方差注释,那么这里的交易是什么?为什么需要方差注释?是真的需要还是有办法绕过它?

1 个答案:

答案 0 :(得分:1)

我认为你只是在F.map上反向提出了仿函数参数。试一试:

  def liftF[F[_], A](x: => F[A])(implicit F: Functor[F]) =
    Suspend[F, A](F.map(x) { Return[F, A] } )

  println(liftF(List(1, 2)))
  "Suspend(List(Return(1), Return(2)))"

请注意,无论采用哪种方式将元素应用于仿函数映射,您都会得到相同的结果(您是版本和scalaz版本):

def mapReverse[F[_], A, B](implicit F: Functor[F]) = {
  (f:A => B) => (fa:F[A]) => F.map(fa)(f)
}

val reverse = mapReverse(F)({
  Return[F, A]
})(x)

val normal = F.map(x)({
  Return[F, A]
})

在这种情况下,reversenormal都是F[Return[F, A]]。应用参数的方式仅在Suspend

的上下文中有用
Suspend[F, A](F.map(x)({ Return[F, A] })) // works great

Suspend[F, A](normal) // fails to compile
Suspend[F, A](reverse) // fails to compile

我敢肯定,如果你仔细研究scala语言规范,你就可以找到为什么类型推断以这种方式工作。如果我不得不猜测,当fa应用于F.map时,您会得到类型为(A => B) => F[B]的函数。因此,编译器可能会查看并看到Suspend接受Free,因此它会使(A => Free[F, A]) => F[Free[F, A]]很快将Return[F, A].apply作为参数。当您以强制键入Return的方式应用参数时,而不是推断函数Free