1

私はscalazライブラリを使用してscalaのモナドで何かをしようとしていますが、サブタイピングでうまく機能させるのに問題があります。

私は自分のモナドを定義することから始めました。簡単にするために、それをアイデンティティモナドとします。

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関数は引数をfromからにアップキャストするだけExtendedId[A]ですId[A]。完全に冗長なはずですが、そうではありません。

なぜこうなった?Id[A]を含むオブジェクトへの暗黙の変換が存在し、からへmapの些細な暗黙の変換が明らかに存在します。では、なぜコンパイラーはそれらを組み合わせることができないのですか?ExtendedId[A]Id[A]

4

1 に答える 1

0

これは、Scalaz がMonad最初の型引数 (より正確には、型コンストラクタの引数) で共変であると定義していないために発生します。つまり、 aは a とはまったくMonad[A]異なる型と見なされます。(共分散と反分散の詳細)Monad[B]A <: B

Monadが不変であるには十分な理由があります。Monad[Id]1 つは、 aが実際には a としても有効であるとコンパイラーに信じ込ませると、Monad[ExtendedId]ある時点で問題に遭遇することになりpureますExtendedIdId返されます。

を定義する以外に、これをきれいに修正する手法はないと思いますMonad[ExtendedId]

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

これは確かに のすべてのサブクラスに対して適切なモナドを返すことができIdます。

于 2011-05-23T07:10:01.807 に答える