Scala子类型+隐式转换问题
我想在scalaz库中使用scala中的monad做些事情,并且在使用子类型工作时遇到了一些麻烦。Scala子类型+隐式转换问题
我已经开始定义自己的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
不是一个单子了。
现在我想用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
不是一个单子,最好我能得到作为输出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]
的简单隐式转换。那么,为什么编译器无法合并它们?
发生这种情况是因为Scalaz未将Monad
定义为在其第一个类型参数(或更准确地说,类型构造函数参数)中协变。换句话说,Monad[A]
被认为是与Monad[B]
完全不同的类型,即使A <: B
。 (More on covariance and contravariance)
有很好的理由为什么Monad
是不变的。一个是:如果你让编译器相信Monad[Id]
实际上也是有效的Monad[ExtendedId]
,那么在某个时刻你肯定会遇到问题 - 其中一个是无论调用哪个pure
,编译器都会推断出结果类型为ExtendedId
,而只有Id
将被退回。
我认为没有干净地解决这一问题的技术 - 比定义Monad[ExtendedId]
,或类似的东西
implicit def idMonad[A[_] <: Id[_]]: Monad[A] = ...
这的确是能够返回正确的单子为Id
所有子类等。
由于'ExtendedId'不是一个monad,我无法定义'Monad [ExtendedId]'''''ExtendedId''没有有用的'bind'实现('printer'值使它不可能)。我能做的是将'ExtendedId [A]'强制转换为'Id [A]',但我不明白编译器为什么不这样做。我的意思是,上传是所有隐式转换的母体,并且它不是隐式执行的。 – Rotsor 2011-05-23 10:56:00
注释'[A Rotsor 2011-05-23 11:02:21
@Rostor你说得对,我弄乱了我的类型参数。我编辑了我的答案。正如我写的,编译器不会像你所描述的那样转换事物,因为“Monad”不是协变的;即“Monad”的定义明确指出,在实践中,即使“B 2011-05-23 11:36:34