1

我正在尝试使用 scalaz 库对 scala 中的 monads 做一些事情,并且在使其与子类型很好地配合使用时遇到了一些麻烦。

我已经开始定义我自己的 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函数只是将其参数向上转换为 from ExtendedId[A]to Id[A]。似乎它应该是完全多余的,但事实并非如此。

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

4

1 回答 1

0

发生这种情况是因为 Scalaz 在其第一个类型参数(或更准确地说,类型构造函数参数)中没有定义Monad为协变的。换句话说, a被认为是与 a 完全不同的类型,即使。(更多关于协变和逆变Monad[A]Monad[B]A <: B

有很好的理由为什么Monad是不变的。一个是:如果你让编译器相信 aMonad[Id]实际上作为 a 也是有效的Monad[ExtendedId],那么你一定会在某些时候遇到问题——其中之一是无论在哪里pure调用,编译器都会推断出ExtendedId结果类型,而只有一个Id将被退回。

认为没有一种技术可以彻底解决这个问题——除了定义 aMonad[ExtendedId]或类似的东西

implicit def idMonad[A[_] <: Id[_]]: Monad[A] = ...

它确实能够为Id.

于 2011-05-23T07:10:01.807 回答