2009-07-16 11 views
5

Ho un codice Scala che fa qualcosa di carino con due diverse versioni di una funzione parametrizzata per tipo. L'ho semplificato molto dalla mia applicazione ma alla fine il mio codice pieno di chiamate del modulo w(f[Int],f[Double]) dove il mio metodo magico è w(). Mi piacerebbe avere un metodo più magico come z(f) = w(f[Int],f[Double]) - ma non riesco a ottenere alcuna sintassi come z(f[Z]:Z->Z) per funzionare come sembra (per me) come argomenti di funzione non possono avere i propri parametri di tipo. Ecco il problema come snippet di codice Scala.Può Scala consentire parametri di tipo libero negli argomenti (sono i parametri di tipo Scala I cittadini di prima classe?)?

Qualche idea? Una macro potrebbe farlo, ma non penso che quelli facciano parte di Scala.

object TypeExample { 
    def main(args: Array[String]):Unit = { 
    def f[X](x:X):X = x    // parameterize fn 
    def v(f:Int=>Int):Unit = { }  // function that operates on an Int to Int function 
    v(f)        // applied, types correct 
    v(f[Int])      // appplied, types correct 
    def w[Z](f:Z=>Z,g:Double=>Double):Unit = {} // function that operates on two functions 
    w(f[Int],f[Double])    // works 
// want something like this: def z[Z](f[Z]:Z=>Z) = w(f[Int],f[Double]) 
// a type parameterized function that takes a single type-parameterized function as an 
// argument and then speicalizes the the argument-function to two different types, 
// i.e. a single-argument version of w() (or wrapper) 
    } 
} 
+0

Si desidera parametrizzare l'argomento di z? –

+0

Sì, vorrei che l'argomento di z() fosse una funzione parametrizzata (come la mia f) e quindi avere z() associare il parametro type a due valori diversi. – jmount

+0

Ciò che sarebbe totalmente rock sarebbe una sorta di funzione che prenderebbe una funzione parametrizzata f e restituirà una classe con un membro impostato su f [Int] e l'altro su f [Double]. – jmount

risposta

6

È possibile farlo in questo modo:

trait Forall { 
    def f[Z] : Z=>Z 
} 

def z(u : Forall) = w(u.f[Int], u.f[Double]) 

o utilizzando tipologie strutturali:

def z(u : {def f[Z] : Z=>Z}) = w(u.f[Int], u.f[Double]) 

ma questo sarà più lento rispetto alla prima versione, in quanto utilizza la riflessione.

EDIT: Questo è come si utilizza la seconda versione:

scala> object f1 {def f[Z] : Z=>Z = x => x} 
defined module f1 

scala> def z(u : {def f[Z] : Z=>Z}) = (u.f[Int](0), u.f[Double](0.0)) 
z: (AnyRef{def f[Z]: (Z) => Z})(Int, Double) 

scala> z(f1) 
res0: (Int, Double) = (0,0.0) 

Per la prima versione aggiungere f1 extends Forall o semplicemente

scala> z(new Forall{def f[Z] : Z=>Z = x => x}) 
+0

Questo sembra ragionevole (usando la rappresentazione dell'oggetto delle funzioni), ma non ne so (ancora) abbastanza su Scala per farlo funzionare. In ogni caso se digiti 'z (f)' ottengo gli errori del tipo di compilazione: argomento mancante per il metodo f; L'espressione polimorfa non può essere istanziata con il tipo atteso; – jmount

+0

Ho aggiunto un esempio di utilizzo. –

+0

È fantastico! Ovviamente quello che hai detto ha senso, non ne sapevo abbastanza per applicarlo. La linea che volevo è def z (u: {def f [Z]: Z => Z}) = (u.f [Int], u.f [Double]) che funziona perfettamente. Il prurito filosofico che volevo soddisfare era scrivere "f" una volta sola (garantire entrambe le specializzazioni sono la stessa funzione), aggiungendo alcune dichiarazioni aggiuntive per raggiungere questo non è affatto un problema. Grazie. – jmount

2

Non penso che quello che vuoi fare sia possibile.

Edit:

La mia versione precedente era difettoso. Questo funziona:

scala> def z(f: Int => Int, g: Double => Double) = w(f, g) 
z: (f: (Int) => Int,g: (Double) => Double)Unit 

scala> z(f, f) 

Ma, naturalmente, è più o meno quello che si ha.

Non penso che sia nemmeno possibile che funzioni, perché i parametri di tipo esistono solo in fase di compilazione. In fase di esecuzione non esiste una cosa del genere. Quindi non ha senso per me passare una funzione parametrizzata,, invece di una funzione con i parametri di tipo dedotti da Scala.

E, no, Scala non ha un sistema macro.

+0

Mi piace la parte in cui Scala inferisce i tipi (che è bello). Grazie per l'aiuto nello studio dello spazio della soluzione. – jmount

6

Se siete curiosi, cosa si sta parlando qui è chiamato "rank-k polymorphism". See wikipedia. Nel tuo caso, k = 2. Alcuni traduzione:

Quando si scrive

f[X](x : X) : X = ... 

allora stai dicendo che f è di tipo "forall XX -> X"

Quello che vuoi per z è di tipo "(per tutti ZZ -> Z) -> Unità". Quella coppia in più di parentesi è una grande differenza. In termini di articolo di wikipedia mette il qualificatore di forall prima di 2 frecce anziché di 1. La variabile di tipo non può essere istanziata solo una volta e portata a termine, deve essere probabilmente istanziata a molti tipi diversi. (Qui "istanziazione" non significa costruzione di oggetti, significa assegnare un tipo a una variabile di tipo per il controllo di tipo).

Come la risposta di alexy_r mostra questo è codificabile in Scala usando oggetti piuttosto che tipi di funzione diritta, essenzialmente usando classi/tratti come i parenti. Anche se sembra aver lasciato voi appeso un po 'in termini di collegarlo nel codice originale, ecco che è:

// questo è il codice

object TypeExample { 
    def main(args: Array[String]):Unit = { 
    def f[X](x:X):X = x    // parameterize fn 
    def v(f:Int=>Int):Unit = { }  // function that operates on an Int to Int function 
    v(f)        // applied, types correct 
    v(f[Int])      // appplied, types correct 
    def w[Z](f:Z=>Z,g:Double=>Double):Unit = {} // function that operates on two functions 
    w(f[Int],f[Double])    // works 

// Questo è il nuovo codice

trait ForAll { 
     def g[X](x : X) : X 
    } 

    def z(forall : ForAll) = w(forall.g[Int], forall.g[Double]) 
    z(new ForAll{def g[X](x : X) = f(x)}) 
    } 
} 
+0

Grazie, James lo esaminerò. Non ho assolutamente alcuna lamentela riguardo al sistema di tipo Scala- Sono disposto a lavorarci per ottenere ciò che voglio. E mi piace che il compilatore mi dica immediatamente cosa non funzionerà. Il mio intento è come hai detto tu - il problema originale (non semplificato) è una funzione che calcola su diversi campi algebrici (alcuni costosi con cui lavorare, alcuni con cui lavorare a buon mercato) e volevo che Scala generasse due funzioni di tipo sicuro specializzate da un modello funzione iniziale Tutte le altre parti mobili erano facili da organizzare, tranne quest'ultima trasformazione di tipo. – jmount

+0

La sintassi 'new ForAll' è più bella di quella che avevo. Ho dimenticato che funziona. –

+0

Collegamento a Wikipedia: http://en.wikipedia.org/wiki/Parametric_polymorphism#Higher-ranked_polymorphism –