Как я могу совместить шаблон typeclass с подтипированием?
Предположим, что я использую шаблон typeclass в Scala. Вот как я делаю класс C
часть класса Foo:
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 a C!") } }
defined module FooC
scala> foo(new C)
it a C!
Пока все хорошо. Но предположим, что у меня есть подкласс D of C, и я хочу, чтобы экземпляры 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! Как выполнить эту работу без явного предоставления экземпляра typeclass для D?
Ответы
Ответ 1
Для этого существуют различные возможные решения, в зависимости от того, хочу ли я исправить проблему только для C, или я хочу исправить проблему для всего класса.
Только для C вместо implicit object FooC ...
мы говорим:
implicit def CIsFoo[T <: C]: Foo[T] =
new Foo[T] { override def foo(t: T) { println("it 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 обитателям Даниэлю Собралу и Стефану Зейгеру за их помощь.)
ОБНОВЛЕНО 20 сентября 2011 года, чтобы включить решение "make Foo contravariant", которое я пропустил