如何将类型类型与子类型结合起来?

Set*_*sue 14 scala typeclass variance

假设我在Scala中使用类型类模式.这是我如何制作类型类Foo的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!
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.但是假设我有一个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)
          ^
Run Code Online (Sandbox Code Playgroud)

卫生署!如何在不显式为D提供类型类实例的情况下完成此工作?

Set*_*sue 15

对此有不同的可能解决方案,具体取决于我是否只想为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!") } }
Run Code Online (Sandbox Code Playgroud)

要修复所有Foo,请使其逆变:

trait Foo[-T] { def foo(t: T) }
Run Code Online (Sandbox Code Playgroud)

或者,如果由于某种原因您不能或不想这样做,您可以替换def foo...为:

def foo[T](t: T)(implicit foo: Foo[_ >: T]) =
  foo.foo(t)
Run Code Online (Sandbox Code Playgroud)

(感谢#scala居民Daniel Sobral和Stefan Zeiger的帮助.)

2011年9月20日更新,包括"错过了make foo逆变"解决方案,我错过了

  • @jsuereth没有什么可以阻止你为`C`声明`D`的'隐含对象FooD`,并且在`FooD`中调用来自`FooC`的方法. (3认同)