假设我们有一个具有协变和逆变类型参数的类:
sealed trait Pipe[-I,+O,+R]
// case subclasses
我们为这个类的实例定义了monadic操作:
object Pipe {
def flatMap[I,O,Ri,R](p: Pipe[I,O,Ri], f: Ri => Pipe[I,O,R]): Pipe[I,O,R] =
...
}
为了能够使用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);
}
但是,这不会编译,它会以
失败逆变类型
I
出现在值(R) => Pipe[I,O,R1]
的{{1}}类型的协变位置。
(协变类型参数也会发生类似的错误。)
我理解限制以及问题发生的原因。但是有一些解决方法,如何使用f
使用与上面相同的语义在特征上定义flatMap
?也许使用一些隐式转换和/或中间构建器类?
答案 0 :(得分:4)
最简单的,
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
,即def point[I, O, A](a: A): Pipe[I, O, A]
,则实施完整的scalaz.Monad
类型类可能很有用,因为Scalaz的含义会给您flatMap
和许多其他免费的monadic操作:
implicit def pipeMonad[I, O] = new Monad[({type λ[α]=Pipe[I, O, α]})#λ] {
// TODO implement point and bind
}