2011-07-13 10 views
13

Supongamos que estoy usando el patrón de clase de clase en Scala. He aquí cómo hago una parte de clase C de la clase de tipos de Foo:¿Cómo puedo combinar el patrón de tipo de letra con el subtipado?

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! 

Hasta aquí todo bien. Pero supongamos que tengo una subclase D de C, y quiero instancias de D a estar "en" la clase de tipos también:

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! ¿Cómo puedo hacer que esto funcione sin tener que proporcionar explícitamente una instancia de clase de tipos para D?

+0

Esto es más o menos un duplicado de http: // stackoverflow. com/questions/3869991/type-class-pattern-in-scala-doesnt-consider-inheritance –

Respuesta

14

Existen diferentes soluciones posibles para esto, dependiendo de si quiero solucionar el problema sólo para C, o si yo quiero arreglar el problema para toda la clase de tipos.

Para C solamente, en lugar de implicit object FooC ... nos dicen:

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

Para solucionar todos Foo, hacen que sea contravariant:

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

O si por alguna razón usted no puede o don' t quiere hacer eso, puede reemplazar con def foo...:

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

(Gracias a #scala habitante . S Daniel Sobral y Stefan Zeiger por su ayuda)

ACTUALIZADO 20 Sep de 2011 para incluir la solución "hacer Foo contravariant", que echaba de menos

+1

Hay otra pregunta al acecho aquí ... ¿Qué pasa si quiero que D sea tratado de manera ligeramente diferente que T, * pero * compartir un código común ! – jsuereth

+3

@jsuereth Nada le impide declarar un 'objeto implícito FooD' para' D' como lo hizo con 'C', y llamar a los métodos de' FooC' en 'FooD'. –

+1

En realidad deberías probarlo. 'Importaciones implícitas en conflicto' a menos que se ensucie con las prioridades. La solución completa parece bastante caldeada y puede ser frustrante. – jsuereth

Cuestiones relacionadas