【问题标题】:How to define flatMap for a class with covariant/contravariant type parameters?如何为具有协变/逆变类型参数的类定义 flatMap?
【发布时间】:2013-02-17 12:38:20
【问题描述】:

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

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 出现在值f 的类型(R) => Pipe[I,O,R1] 的协变位置。

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

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

【问题讨论】:

    标签: scala monads covariance contravariance for-comprehension


    【解决方案1】:

    最简单的,

    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 和许多其他单子操作:

    implicit def pipeMonad[I, O] = new Monad[({type λ[α]=Pipe[I, O, α]})#λ] {
      // TODO implement point and bind
    }
    

    【讨论】:

      猜你喜欢
      • 2012-04-07
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 1970-01-01
      • 2013-11-29
      • 1970-01-01
      • 2013-05-19
      • 1970-01-01
      相关资源
      最近更新 更多