2014-09-03 3 views
5

Sagen wir also, ich möchte einen benutzerdefinierten Funktionstyp namens ImportFunc erstellen, der einen Int namens fileImportID und einen String namens filename annimmt. Ich kann dies tun ziemlich leicht eine Art Alias ​​wie folgt:Wie können Sie benutzerdefinierte Funktionstypen in Scala mit benannten Parametern erstellen?

type ImportFunc = (Int, String) => Unit 

Das Problem ist, jemand versucht, diese Funktion zu nutzen hat keine Ahnung, was Int und String sind tatsächlich soll. Gibt es irgendeine Weise, die ich so etwas wie schreiben:

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

Antwort

3

Wenn Sie eine Funktion aufrufen, rufen Sie tatsächlich die Apply-Methode der Funktion auf. Mit anderen Worten, da dies:

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

folgenden Ausschnitt:

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

... ist nur syntaktischer Zucker für:

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

Wenn es einen Ort gibt, wo der Compiler sucht bei einem Aufruf mit benannten Parametern nach den Namen der Argumente, das ist notwendigerweise in der Definition der Methode apply. Es stellt sich heraus, dass in Function2 diese Argumente v1 und v2 genannt werden. So können wir tun:

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

Nun wollen wir sehen, ob es noch funktioniert, wenn die syntaktische Zucker mit (in anderen Worten, wenn die expliziten Aufruf apply entfernen):

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

Nizza, es funktioniert. Jetzt natürlich v1 und v2 ist nicht ganz dasselbe wie fileImportID und filename, aber wir können mit etwas Art Verfeinerung, dass beheben:

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

Im Grunde ist dies nur (Int, String)=>Unit (oder mit anderen Worten Function2[Int, String, Unit]), aber mit eine Neudefinition von apply mit unseren gewünschten Argumentnamen. die in Aktion sehen dies Lassen:

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

Erfolg!

Eine wichtige Randnotiz: In Bezug auf die Typisierung ist ImportFunc identisch mit Function2[Int, String, Unit] oder zu irgendeiner anderen ähnlichen Verfeinerung. Dies liegt daran, dass Argumentnamen nicht Teil der Signatur sind.Also in meinem Beispiel f kann immer noch irgendwo eine Function2[Int, String, Unit] erwartet werden (aber von diesem Punkt werden Sie nicht mehr in der Lage sein, es mit Ihren benutzerdefinierten Argumentnamen aufzurufen).

2

In Scala, Funktionen definiert werden aus Zügen FunctionX, so können Sie tun, wie folgend:

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

Eine einfache "Typ" Lösung:

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

Nicht gerne Case-Klasse zu Wert für die Namensgebung wrapping – cchantep

+0

Ich zog dies zu einer anderen Antwort. – skytteren

+0

Ich bin nicht sicher, warum Sie es auf eine andere Antwort verschoben haben, außer vielleicht versuchen, mehr Rep zu bekommen. Warum halten Sie nicht beide Optionen in der gleichen, da sie sich so ähnlich sind? – kingdamian42

-1

Ich bin nicht allzu lieb von Int und String, da sie leicht mit anderen Strings und Ints verwechselt werden können. Do:

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

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

Ich benutze Typen für fast alles. Ich tendiere dazu, bei jedem Funktionsaufruf, bei dem mehr als zwei Parameter gleich sind, Strings, Ints und die Standard-AnyVals zu mischen. Also keine Strings und Ints in freier Wildbahn. – skytteren

+1

Sie haben vielleicht geahnt, dass jemand bereits einen Namen für dieses Muster erfunden hat: http://darrenhobbs.com/2007/04/11/tiny-types/, http://www.markhneedham.com/blog/2009/03/10/oo-Mikrotypen /, http://andypalmer.com/2009/07/tiny-types/, http://philcalcado.com/2009/08/21/ubiquitous-language-tiny-types-and Verantwortlichkeit /, http://grahamnash.blogspot.de/2011/08/tiny-type-language-support.html. –

Verwandte Themen