如何为具有协变/逆变类型参数的类定义 flatMap?

Pet*_*lák 1 monads scala covariance contravariance for-comprehension

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

sealed trait Pipe[-I,+O,+R]
// case subclasses
Run Code Online (Sandbox Code Playgroud)

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

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

为了能够使用for-理解,我们需要它flatMap是特征本身的方法:

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);
}
Run Code Online (Sandbox Code Playgroud)

但是,这不会编译,失败并显示

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

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

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

Ben*_*mes 5

最简单的是,

\n\n
implicit def pipeFlatMap[I, O, A](pipe: Pipe[I, O, A]) = new {\n  def flatMap[B](f: A => Pipe[I, O, B]) = Pipe.flatMap(pipe, f)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
implicit def pipeMonad[I, O] = new Monad[({type \xce\xbb[\xce\xb1]=Pipe[I, O, \xce\xb1]})#\xce\xbb] {\n  // TODO implement point and bind\n}\n
Run Code Online (Sandbox Code Playgroud)\n