2012-03-20 3 views
1

私はタイプクラスを定義する次のコードを持っています。タイプクリティカルとサブタイプ

trait Foo[T] { 
    def toFoo(x: T): String 
} 

trait Foos { 
    def toFoo[T](f: T => String): Foo[T] = new Foo[T] { 
    def toFoo(x: T): String = f(x) 
    } 
} 

object Foo extends Foos { 
    def toFoo[A: Foo](a: A) = implicitly[Foo[A]].toFoo(a) 
    implicit def AToFoo: Foo[A] = toFoo { c => 
    "A" 
    } 
    implicit def BToFoo[T]: Foo[B] = toFoo { c => 
    "B" 
    } 

    implicit def ListToFoo[T: Foo]: Foo[List[T]] = toFoo { c => 
    c.map(toFoo(_)). 
    } 
} 

class A 
class B extends A 

は、今私はtoFoo(List(new A, new B)をすれば、私が持っている場合、私はList("A", "A")の代わりList("A", "B")取得します。タイプBのクラスに対してAToFooではなくBtoFooメソッドが使用されるようにするにはどうすればよいですか?

+0

'List(new A、new B)'にはどんなタイプがありますか? –

+0

'List(A、new B)'の型が 'List [A]' – Stephan

+0

であると期待している場合、 'List [A]'に 'toFoo'をマップすると、'暗黙[Foo [A]] 'を呼び出して、' AToFoo'暗黙のdefを取得し、それを各要素に使用します。型抜きは、型指向のディスパッチのために設計されたものであり、バリューディレクティブのディスパッチではありません。したがって、サブタイプ化で常にうまくいくとは限りません。 –

答えて

2

暗黙の解決は純粋にコンパイル時のメカニズムです。ここでサブタイプを区別する簡単な方法は、暗黙的に内部を照合することです。どちらの場合ももちろん

implicit val AIsFoo : Foo[A] = toFoo { 
    case b: B => "B" 
    case a: A => "A" 
} 

で完全BToFooを削除し、代わりにAバージョンの契約を持っている、階層の一部のための方法に委譲することも可能です。 Aのメソッドに委譲し、Bをオーバーライドしても、メソッドを追加できないListのtypeclassを使用することができます。

また、Fooと反例があると宣言することもできます。

+0

私はパターンマッチングを使ってあなたの提案を実装しましたが、 'XIsFoo'を子どもの' X'に直接書くのではなく、 'AIsFoo'の' A'のすべての子をチェックする必要がありません。 A. Fooの反変がどのように働くのですか?私は試しました 'trait Foo [-T] { def toFoo(x:T):String } – Stephan

関連する問題