Как определить flatMap для класса с параметрами ковариантного / контравариантного типа?

Допустим, у нас есть класс с ковариантным и контравариантным параметрами типа:

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-computing, нам нужно, чтобы 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] значения f.

(Аналогичная ошибка возникает и для параметров ковариантного типа.)

Я понимаю ограничение и почему возникает проблема. Но есть ли обходной путь, как определить flatMap для признака, используя Pipes.flatMap с той же семантикой, что и выше? Возможно, с использованием некоторых неявных преобразований и / или промежуточного класса построителя?


person Petr    schedule 03.03.2013    source источник


Ответы (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
}
person Ben James    schedule 03.03.2013