1

假设我们有一个具有协变和逆变类型参数的类:

sealed trait Pipe[-I,+O,+R]
// case subclasses

我们为这个类的实例定义了单子操作:

object Pipe {
    def flatMap[I,O,Ri,R](p: Pipe[I,O,Ri], f: Ri => Pipe[I,O,R]): Pipe[I,O,R] =
        ...
}

为了能够使用for-comprehension,我们需要它flatMap是 trait 本身的一个方法:

sealed trait Pipe[-I,+O,+R] {
    def flatMap[I,O,Ri,R](f: Ri => Pipe[I,O,R]): Pipe[I,O,R] =
        Pipe.flatMap(this, f);
}

但是,这不会编译,它失败了

逆变类型I出现在(R) => Pipe[I,O,R1]value 类型的协变位置f

(协变类型参数也会出现类似的错误。)

我了解限制以及问题发生的原因。但是是否有一些解决方法,如何flatMap使用Pipes.flatMap与上述相同的语义来定义特征?也许使用一些隐式转换和/或中间构建器类?

4

1 回答 1

5

最简单的,

implicit def pipeFlatMap[I, O, A](pipe: Pipe[I, O, A]) = new {
  def flatMap[B](f: A => Pipe[I, O, B]) = Pipe.flatMap(pipe, f)
}

如果您Pipe允许实现point, ie def point[I, O, A](a: A): Pipe[I, O, A],那么实现一个完整的scalaz.Monad类型类可能会很有用,因为 Scalaz 的隐式将为您flatMap和许多其他单子操作免费提供:

implicit def pipeMonad[I, O] = new Monad[({type λ[α]=Pipe[I, O, α]})#λ] {
  // TODO implement point and bind
}
于 2013-03-03T10:28:24.543 回答