2011-07-13 31 views
13

假設我在Scala中使用了typeclass模式。以下是我做的類型類美孚的C類 部分:如何將類型類型與子類型相結合?

Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26). 

scala> trait Foo[T] { def foo(t: T) } 
defined trait Foo 

scala> def foo[T : Foo](t: T) { implicitly[Foo[T]].foo(t) } 
foo: [T](t: T)(implicit evidence$1: Foo[T])Unit 

scala> class C 
defined class C 

scala> foo(new C) 
<console>:11: error: could not find implicit value for evidence parameter of type Foo[C] 
     foo(new C) 
     ^

scala> implicit object FooC extends Foo[C] { override def foo(c: C) { println("it's a C!") } } 
defined module FooC 

scala> foo(new C) 
it's a C! 

到目前爲止好。但是,假設我有C的子類D,並且我希望D的實例也處於「類」類型中:

scala> class D extends C 
defined class D 

scala> foo(new D) 
<console>:13: error: could not find implicit value for evidence parameter of type Foo[D] 
     foo(new D) 
     ^

Doh!如何在不需要爲D提供類型實例的情況下進行這項工作?

+0

這是非常HTTP的副本://計算器。 com/questions/3869991/type-class-pattern-in-scala-doesnt-consider-inheritance –

回答

14

有此不同的解決方案,這取決於我是否要解決這個問題只對C,或者我是否要解決這個問題整個類型類

對於C只,而不是implicit object FooC ...我們說:

implicit def CIsFoo[T <: C]: Foo[T] = 
    new Foo[T] { override def foo(t: T) { println("it's a C!") } } 

解決所有的Foo,使逆變:

trait Foo[-T] { def foo(t: T) } 

或者,如果你因爲某些原因不能或不要」噸要做到這一點,你可以用替換def foo...

def foo[T](t: T)(implicit foo: Foo[_ >: T]) = 
    foo.foo(t) 

(感謝#scala常客。Daniel在索布拉爾和Stefan Zeiger提供的幫助)

修訂 2011年9月20日包括「使富逆變」的解決方案,我錯過了

+1

還有另一個問題潛伏在這裏...如果我想讓D被處理的方式與T稍有不同,*但是*共享一些通用的代碼! – jsuereth

+3

@jsuereth沒有什麼能夠阻止你爲'D'聲明'隱式對象FooD',就像你爲'C'所做的那樣,並且從'FooD'中的'FooC'調用方法。 –

+1

其實你應該嘗試一下。 '衝突的隱式進口',除非你有優先權。完整的解決方案看起來很平淡,可能會令人沮喪。 – jsuereth