2011-07-13 33 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