2014-09-03 12 views
5

Quindi supponiamo di voler creare un tipo di funzione personalizzato chiamato ImportFunc che accetta un Int chiamato fileImportID e una stringa chiamata nomefile. Posso fare questo abbastanza facilmente utilizzando un tipo alias in questo modo:Come è possibile creare tipi di funzione personalizzati in Scala con i parametri denominati?

type ImportFunc = (Int, String) => Unit 

Il problema è che nessuno di tentare di usare questa funzione non ha idea di cosa Int e String sono in realtà dovrebbero essere. C'è qualche modo posso scrivere qualcosa di simile:

type ImportFunc = (fileImportID: Int, filename: String) => Unit 

risposta

3

Quando si chiama una funzione, si chiama effettivamente il metodo apply della funzione. In altre parole, dato questo:

def doImport(fileImportID: Int, filename: String) { 
    println(s"Importing file #$fileImportID ($filename)") 
} 

il seguente frammento:

val f = doImport _ 
f(123, "file.txt") 

... è zucchero sintattico per:

val f = doImport _ 
f.apply(123, "file.txt") 

Se c'è un luogo in cui il compilatore cercherà i nomi degli argomenti durante una chiamata con parametri denominati, che è necessariamente nella definizione del metodo apply. Si scopre che in Function2, tali argomenti sono denominati v1 e v2. Così possiamo fare:

scala> f.apply(v1=123, v2="file.txt") 
Importing file #123 (file.txt) 

Ora vediamo se funziona ancora quando si utilizza lo zucchero sintattico (in altre parole, quando si rimuove la chiamata esplicita a apply):

scala> f(v1=123, v2="file.txt") 
Importing file #123 (file.txt) 

Nizza, funziona. Ora, naturalmente, v1 e v2 non è proprio la stessa fileImportID e filename, ma possiamo rimediare con un po 'di tipo raffinatezza:

type ImportFunc = ((Int, String)=>Unit) { 
    def apply(fileImportID: Int, filename: String): Unit 
} 

Fondamentalmente questo è solo (Int, String)=>Unit (o in altre parole Function2[Int, String, Unit]), ma con una ridefinizione di apply con i nostri nomi argomento desiderati. Vediamo questo in azione:

scala> val f: ImportFunc = doImport _ 
f: ImportFunc = <function2> 
scala> f(fileImportID=123, filename="file.txt") 
Importing file #123 (file.txt) 

successo!

Un'importante nota a margine: in termini di digitazione, lo ImportFunc è identico allo Function2[Int, String, Unit] oa qualsiasi altro raffinamento simile. Questo perché i nomi degli argomenti non fanno parte della firma.Quindi nel mio esempio f può ancora essere passato ovunque sia previsto un Function2[Int, String, Unit] (ma da quel punto non sarà più possibile chiamarlo usando i nomi degli argomenti personalizzati).

2

In Scala, funzioni sono definite dai tratti FunctionX, in modo da poter fare come segue:

trait ImportFunc extends ((Int, String) => Unit) { 
    def apply(fileImportId: Int, filename: String): Unit 
} 

// Then custom definition can be implemented as following 
val f1: ImportFunc = new ImportFunc { 
    def apply(fid: Int, fn: String): Unit = ??? 
} 
f1(1, "name") // call it 

/** Companion object to ease the use */ 
object ImportFunc { 
    /** Function factory: take a plain (Int, String) => Unit 
    and turn it into documented type */ 
    def apply(f: (Int, String) => Unit): ImportFunc = new ImportFunc { 
    def apply(fileImportId: Int, filename: String): Unit = f(fileImportId, filename) 
    } 
} 

val f2: ImportFunc = ImportFunc((fid: Int, fn: String) => ???) 
f2(2, "eman") // call it 
1

Una soluzione semplice "tipo":

type FileImportID = Int 
type Filename = String 
type ImportFunc = (FileImportID, Filename) => Unit 
+0

Non mi piace la classe case per avvolgere il valore per lo scopo di denominazione – cchantep

+0

L'ho spostato su un'altra risposta. – skytteren

+0

Non sono sicuro del motivo per cui l'hai spostato su un'altra risposta, se non per cercare di ottenere più risposte. Perché non mantenere entrambe le opzioni nella stessa poiché sono così simili. – kingdamian42

-1

Non sono troppo appassionato di Int e String poichè sono facili da mescolare con altre stringhe e stringhe. Do:

case class FileImportID(value: Int) extends AnyVal 
case class Filename(value: String) extends AnyVal 

//Leading to 
type ImportFunc = (FileImportID, Filename) => Unit 
+0

Io uso i tipi per quasi tutto. Tendo a mescolare String, Ints e gli AnyVals standard in qualsiasi chiamata di funzione in cui ci sono più di due parametri uguali. Quindi niente stringhe e Ints allo stato selvatico. – skytteren

+1

Potresti aver intuito che qualcuno ha già inventato un nome per questo modello: http://darrenhobbs.com/2007/04/11/tiny-types/, http://www.markhneedham.com/blog/2009/03/10/oo-micro-types /, http://andypalmer.com/2009/07/tiny-types/, http://philcalcado.com/2009/08/21/ubiquitous-language-tiny-types-and -responsibility /, http://grahamnash.blogspot.de/2011/08/tiny-type-language-support.html. –

Problemi correlati