Подтип Scala + проблема неявного преобразования - PullRequest
1 голос
/ 23 мая 2011

Я пытаюсь что-то сделать с монадами в scala, используя библиотеку scalaz, и у меня возникают проблемы с тем, чтобы он хорошо работал с подтипами.

Я начал с определения моей собственной монады. Пусть это будет монада идентичности ради простоты:

import scalaz._
import Scalaz._

class Id[+A] (val value : A) { }

implicit object IdMonad extends Monad[Id] {
    override def pure[A](a : => A) = new Id(a)
    override def bind[A, B](a : Id[A], f : A => Id[B]) = f(a.value)
}

Далее я расширил его некоторыми дополнительными функциями:

class ExtendedId[A] (value : A, val printer : A => String) extends Id[A](value) { }

С этой дополнительной функциональностью ExtendedId больше не является монадой.

Теперь я хочу использовать объект типа ExtendedId[A] как Id[A]:

def increment1(v : ExtendedId[Int]) : Id[Int] = {
    for(v <- v) yield v + 1;
    //    ^
    //  error: could not find implicit value for parameter t:  scalaz.Functor[test.package.ExtendedId]
}

Обратите внимание, что я понимаю, что, поскольку ExtendedId не является монадой, лучшее, что я могу получить в качестве результата, это Id[Int], и я согласен с этим! Но, к сожалению, этот код все еще не компилируется.

Однако этот делает:

def asId[A](a : ExtendedId[A]) : Id[A] = a

def increment2(v : ExtendedId[Int]) {
    for(v <- asId(v)) yield v + 1;
}

Здесь, функция asId не делает ничего, кроме как передвигает свой аргумент с ExtendedId[A] до Id[A]. Кажется, что он должен быть полностью избыточным, но это не так.

Почему это происходит? Существует неявное преобразование из Id[A] в объект, содержащий map, и, очевидно, существует тривиальное неявное преобразование из ExtendedId[A] в Id[A]. Итак, почему компилятор не может объединить их?

1 Ответ

0 голосов
/ 23 мая 2011

Это происходит потому, что Scalaz не определяет Monad как ковариантный в своем первом аргументе типа (или, точнее, конструктор типа аргумент). Другими словами, Monad[A] считается совсем другим типом, чем Monad[B], даже если A <: B. ( Подробнее о ковариации и контравариантности )

Существуют веские причины, по которым Monad является инвариантом. Один из них: если вы заставите компилятор поверить, что Monad[Id] на самом деле также действителен как Monad[ExtendedId], вы неизбежно столкнетесь с проблемами в какой-то момент - один из них заключается в том, что везде, где вызывается pure, компилятор выведет ExtendedId тип результата, тогда как будет возвращен только Id.

Я думаю, не существует техники, чтобы исправить это чисто - кроме определения Monad[ExtendedId], или что-то вроде

implicit def idMonad[A[_] <: Id[_]]: Monad[A] = ...

, который действительно может вернуть правильную монаду для всех подклассов Id.

...