2011-11-25 17 views
7

Il seguente è possibile in Scala:Scala costruttore astrazione

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

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

In altre parole, Scala ha ordine superiore polimorfismo. Mi piacerebbe usare il polimorfismo di ordine superiore per fare quanto segue.

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 { ... } 
.... 

quindi ho un sacco di classi case, sottoclassi di A, i cui costruttori non necessariamente prendere lo stesso numero di argomenti. Mi piacerebbe anche avere una 'generica' classe caso, qualcosa di simile:

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

L'idea è che ApplyA prende come primo argomento un costruttore per qualcosa che è un sottotipo di A, e un elenco di argomenti. Il metodo eval quindi costruisce una classe appropriata con il costruttore, se possibile (ovvero l'elenco ha la lunghezza giusta) e lo restituisce (questo corrisponde a l (1, 2, 3) nell'esempio List precedente). Quale sarebbe il tipo di argomento del primo costruttore per ApplyA?

Questo dovrebbe essere possibile con il polimorfismo di ordine superiore, ma non ho potuto capire come. So che posso farlo anche senza usare il polimorfismo di ordine superiore semplicemente avvolgendo i costruttori in funzioni e poi passando queste funzioni come primo argomento al costruttore per ApplyA, ma mi piacerebbe capire come usare direttamente il polimorfismo di ordine superiore.

risposta

9

Il problema è che l'esempio List non comporta alcun polimorfismo di ordine superiore. List.apply richiede solo un numero variabile di parametri:

def apply(xs: A*) 

ordine superiore polimorfismo coinvolge metodi o tipi, che prendono costruttori di tipo come parametri di tipo, ad esempio

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

Quindi no, non è possibile farlo utilizzando il polimorfismo di ordine superiore.

11

@alexey_r ha ragione che l'esempio List non coinvolge il polimorfismo di ordine superiore. Ma se sei pronto a usare un po 'di type-level heavy artillery puoi astrarre l'arità dei tuoi costruttori A{0,1,2} per ottenere qualcosa che sembra abbastanza vicino a quello che stai chiedendo.

Il primo punto da notare è che, come scritto, la classe 'generico' non può assolutamente essere attuata,

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

perché non c'è alcuna relazione verificabile momento della compilazione tra l'arity del costruttore c e la lunghezza della lista l. Siamo in grado di risolvere il problema sostituendo il List con una HList e aiutare noi stessi ad una conversione dalle funzioni ordinarie arity arbitrario funzioni con un singolo HList argomento,

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) 

L'argomento implicito hl : FnHListerAux[C, HF] fornisce una conversione dal costruttore, qualunque sia l'arità, a una funzione da un singolo argomento HList.E l'argomento implicito ev : HF <:< (L => A) testimonia che la lunghezza degli argomenti HList forniti dal costruttore è della lunghezza corretta (e digita FWIW, ma questo è appena rilevante in questo esempio).

Problemi correlati