Question

Let's say we have a class that has a covariant and a contravariant type parameter:

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

And we have monadic operations defined for instances of this class:

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

To be able to use for-comprehension, we need that flatMap is a method of the trait itself:

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);
}

However, this does not compile, it fails with

contravariant type I occurs in covariant position in type (R) => Pipe[I,O,R1] of value f.

(A similar error occurs for covariant type parameters as well.)

I understand the restriction and why the problem occurs. But is there some workaround, how to define flatMap on the trait using Pipes.flatMap with the same semantics as above? Perhaps using some implicit conversions and/or an intermediate builder class?

Was it helpful?

Solution

Most simply,

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)
}

If your Pipe permits implementing point, i.e. def point[I, O, A](a: A): Pipe[I, O, A], then implementing a full scalaz.Monad typeclass could be useful, as Scalaz's implicits would give you flatMap and many other monadic operations for free:

implicit def pipeMonad[I, O] = new Monad[({type λ[α]=Pipe[I, O, α]})#λ] {
  // TODO implement point and bind
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top