2014-09-03 128 views
5

假設我想創建一個名爲ImportFunc的自定義函數類型,它接受一個名爲fileImportID的Int和一個名爲filename的字符串。我可以用一個類型別名,像這樣做很容易地:如何使用命名參數在Scala中創建自定義函數類型?

type ImportFunc = (Int, String) => Unit 

的問題是,任何人試圖利用這個功能根本不知道int和string實際上應該是。有一些方法可以讓我寫的東西,如:

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

回答

3

當你調用一個函數,你實際上調用函數的apply方法。 換句話說,考慮到本:

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

下面的代碼片段:

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

...是隻是語法糖:

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

如果有一個地方,編譯器將在使用命名參數進行調用時查找參數的名稱,這必然在apply方法的定義中。 事實證明,在Function2,這些參數被命名爲v1v2。因此,我們可以這樣做:

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

現在讓我們來看看它(去除顯式調用apply當換句話說)使用語法糖時仍然有效:

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

尼斯,它的工作原理。 現在當然v1v2是不太一樣的fileImportIDfilename,但我們可以解決這個帶着幾分類型細化:

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

基本上這只是(Int, String)=>Unit(或者換句話說Function2[Int, String, Unit]),但用我們想要的參數名稱重新定義apply。 讓我們看看這個行動:

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

成功!

重要的一點是:在打字方面,ImportFuncFunction2[Int, String, Unit]相同,或與任何其他類似的改進相同。 這是因爲參數名稱不是簽名的一部分。因此,在我的示例中,f仍然可以通過任何一個Function2[Int, String, Unit]預計 (但從那時起,您將無法再使用自定義參數名稱來調用它)。

2

在Scala中,功能從特質FunctionX定義,所以你可以做如下:

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

一個簡單的「類型」的解決方案:

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

不喜歡爲命名目的包裝價值的案例類 – cchantep 2014-09-03 13:56:21

+0

我將其移至另一個答案。 – skytteren 2014-09-03 14:03:10

+0

我不知道爲什麼你將它移到另一個答案,除了可能試圖獲得更多的代表。爲什麼不把兩個選項都放在同一個選項中,因爲它們非常相似。 – kingdamian42 2014-09-03 14:20:25

-1

我不喜歡Int和String,因爲它們很容易與其他字符串和Ints混淆。做:

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

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

我幾乎所有的東西都使用類型。我傾向於在任何函數調用中混合使用Strings,Ints和標準AnyVals,其中有兩個以上的參數是相同的。所以在野外沒有Strings和Ints。 – skytteren 2014-09-03 14:13:23

+1

您可能已經猜到某人已經爲該模式創建了名稱: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。 – 2014-09-03 17:31:27

相關問題