Scala子类型+隐式转换问题

时间:2011-05-23 02:13:43

标签: scala implicit-conversion scalaz subtyping

我正在尝试使用scalaz库在scala中使用monad做一些事情,并且在使用子类型很好地工作时遇到一些麻烦。

我已经开始定义自己的monad。为了简单起见,让它成为一个身份monad:

import scalaz._
import Scalaz._

class Id[+A] (val value : A) { }

implicit object IdMonad extends Monad[Id] {
    override def pure[A](a : => A) = new Id(a)
    override def bind[A, B](a : Id[A], f : A => Id[B]) = f(a.value)
}

接下来,我扩展了一些额外的功能:

class ExtendedId[A] (value : A, val printer : A => String) extends Id[A](value) { }

使用此附加功能,ExtendedId不再是monad。

现在我想将ExtendedId[A]类型的对象用作Id[A]

def increment1(v : ExtendedId[Int]) : Id[Int] = {
    for(v <- v) yield v + 1;
    //    ^
    //  error: could not find implicit value for parameter t:  scalaz.Functor[test.package.ExtendedId]
}

请注意,我理解,因为ExtendedId不是monad,所以我作为输出得到的最好的是Id[Int],我很好!但不幸的是,该代码仍然无法编译。

然而,这个确实:

def asId[A](a : ExtendedId[A]) : Id[A] = a

def increment2(v : ExtendedId[Int]) {
    for(v <- asId(v)) yield v + 1;
}

此处,asId函数只是将其参数从ExtendedId[A]转换为Id[A]。它似乎应该是完全多余的,但事实并非如此。

为什么会这样?确实存在从Id[A]到包含map的对象的隐式转换,并且显然存在从ExtendedId[A]Id[A]的简单隐式转换。那么,为什么编译器无法将它们组合起来呢?

1 个答案:

答案 0 :(得分:0)

这是因为Scalaz没有将Monad定义为其第一个类型参数(或更确切地说,类型构造函数参数)的协变。换句话说,即使Monad[A]Monad[B]也被视为与A <: B完全不同的类型。 (More on covariance and contravariance

Monad不变的原因很充分。一个是:如果你让编译器认为Monad[Id]实际上也有效Monad[ExtendedId],那么你必然会遇到问题 - 其中一个问题就是{{1}调用时,编译器将推断出pure结果类型,而只返回ExtendedId

认为除了定义Id或类似

之外,没有一种技术可以彻底解决这个问题。
Monad[ExtendedId]

确实可以为implicit def idMonad[A[_] <: Id[_]]: Monad[A] = ... 的所有子类返回一个合适的monad。