2011-11-25 12 views
7

La siguiente es posible en Scala:Scala abstracción constructor

scala> val l = List 
l: scala.collection.immutable.List.type = [email protected] 

scala> l (1, 2, 3) 
res0: List[Int] = List(1, 2, 3) 

En otras palabras, Scala tiene polimorfismo de orden superior. Me gustaría usar el polimorfismo de orden superior para hacer lo siguiente.

sealed abstract class A { def eval() : A } 
case class A0() extends A { ... } 
case class A1 (a : A) extends A { ... } 
case class A2 (a : A, b : A) extends A { ... } 
.... 

así que tengo un montón de clases de casos, las subclases de A, cuyos constructores no necesariamente tener el mismo número de argumentos. También me gustaría tener una clase de caso 'genérico', algo como esto:

case class ApplyA (c : ???, l : List [ A ]) extends A { 
    def eval() : A = { ??? } } 

La idea es que ApplyA toma como primer argumento un constructor para algo que es un subtipo de A, y una lista de argumentos. El método eval luego construye una clase apropiada con el constructor si es posible (es decir, la lista tiene la longitud correcta) y la devuelve (esto corresponde a l (1, 2, 3) en el ejemplo anterior List). ¿Cuál sería el tipo de argumento del primer constructor para ApplyA?

Esto debería ser posible con un polimorfismo de orden superior, pero no pude averiguar cómo. Sé que puedo hacer esto incluso sin usar un polimorfismo de orden superior simplemente envolviendo constructores en funciones y pasando estas funciones como primer argumento al constructor para ApplyA, pero me gustaría entender cómo usar el polimorfismo de orden superior directamente.

Respuesta

9

El problema es que el ejemplo List no implica ningún polimorfismo de orden superior. List.apply sólo se necesita un número variable de parámetros:

polimorfismo
def apply(xs: A*) 

de orden superior implica métodos, o tipos, que tienen constructores de tipos como parámetros de tipo, por ejemplo

def fmap[F[_], A](x: F[A]): F[B] 

Así que no, no puedes hacerlo usando polimorfismo de orden superior.

11

@alexey_r es cierto que su ejemplo List no implica polimorfismo de orden superior. Pero si está preparado para usar un poco de type-level heavy artillery puede abstraer la aridez de sus constructores A{0,1,2} para obtener algo que se asemeje bastante a lo que está pidiendo.

El primer punto a destacar es que, como está escrito, su clase de 'genérico' no puede posiblemente ser implementado,

case class ApplyA(c : ???, l : List[A]) ... 

porque no hay ninguna relación comprobable tiempo de compilación entre la aridad del constructor c y la longitud de la lista l. Podemos solucionar ese problema mediante la sustitución de la List con un HList y ayudarnos a nosotros mismos a una conversión de funciones ordinarias con aridad arbitraria de funciones con un solo argumento HList,

import shapeless.HList._ 
import shapeless.Functions._ 

sealed abstract class A { def eval() : A } 
case class A0() extends A { def eval() = this } 
case class A1 (a : A) extends A { def eval() = this } 
case class A2 (a : A, b : A) extends A { def eval() = this } 

case class ApplyA[C, L <: HList, HF](c : C, l : L) 
    (implicit hl : FnHListerAux[C, HF], ev : HF <:< (L => A)) extends A { 
    def eval() : A = hl(c)(l) 
    } 

val a : A = A0() 

val a0 = ApplyA(A0.apply _, HNil) 
val a1 = ApplyA(A1.apply _, a :: HNil) 
val a2 = ApplyA(A2.apply _, a :: a :: HNil) 

El argumento implícito hl : FnHListerAux[C, HF] proporciona una conversión de su constructor, sea ​​lo que sea, a una función de un solo argumento HList.Y el argumento implícito ev : HF <:< (L => A) atestigua que la longitud del HList proporcionado de los argumentos constructor es de la longitud correcta (y tipos FWIW, pero eso apenas es relevante en este ejemplo).

Cuestiones relacionadas