2011-09-21 70 views
42

我正在按照斯卡拉composeandThen方法的教程Pattern matching & functional composition。有這樣一個例子:撰寫和然後方法

scala> def addUmm(x: String) = x + " umm" 
scala> def addAhem(x: String) = x + " ahem" 

val ummThenAhem = addAhem(_).compose(addUmm(_)) 

當我嘗試使用它,我得到一個錯誤:

<console>:7: error: missing parameter type for expanded function ((x$1) => addAhem(x$1).compose(((x$2) => addUmm(x$2)))) 
    val ummThenAhem = addAhem(_).compose(addUmm(_)) 
          ^
<console>:7: error: missing parameter type for expanded function ((x$2) => addUmm(x$2)) 
    val ummThenAhem = addAhem(_).compose(addUmm(_)) 
              ^
<console>:7: error: type mismatch; 
found : java.lang.String 
required: Int 
    val ummThenAhem = addAhem(_).compose(addUmm(_)) 

然而,這個工程:

val ummThenAhem = addAhem _ compose addUmm _ 

甚至

val ummThenAhem = addAhem _ compose addUmm 

教程中的代碼有什麼問題?不是後者的表達與沒有括號的第一個表達式相同嗎?

回答

38

addAhem是一種方法。 compose方法在函數上定義。 addAhem _addAhem從方法轉換爲函數,因此可以調用composecompose需要一個函數,因爲它是參數。通過將addUmm轉換爲addUmm _(下劃線可以省略,因爲編譯器可以在知道函數預期時自動將函數轉換爲函數),您正在給它一個方法addUmm。所以,你的代碼:

addAhem _ compose addUmm 

相同

(addAhem _).compose(addUmm) 

但不

addAhem(_).compose(addUmm(_)) 

PS 我不敢看你提供的鏈接。

+0

爲了完整起見,andThen例子是這樣的:。 'VAL ahemThenUmm = addAhem(_)andThen(addUmm(_))' 時,它應該像 'VAL ahemThenUmm1 =(addAhem _)andThen(addUmm )' –

+0

我不太確定用圓括號寫的部分;編譯器*不會*將方法自動轉換爲函數,至少對於Scala 2.10.2來說。解決方法是聲明'addAhem'和'addUmm'作爲函數,這樣'compose'或'和Then'就不用'_'工作。 –

5

compose文檔:

Composes two instances of Function1 in a new Function1, with this function applied last.

,所以你應該寫

scala> val ummThenAhem = (addAhem _).compose(addUmm _) 
ummThenAhem: String => java.lang.String = <function1> 

治療addAhemaddUmm爲部分應用功能(即function1

scala> addAhem _ 
res0: String => java.lang.String = <function1> 
2

我相信本教程是爲早期版本的Scala編寫的(可能是2.7.7或更早版本)。目前已在編譯器的一些變化從那時起,即擴展類型系統,這導致現在的類型推斷失敗的:

addUhum(_).compose(addAhem(_)) 

提升到一個功能仍然有效,與語法如果你只是寫:

addUhum(_) 
45

那麼,這:

addUhum _ 

是ETA擴張。它將方法轉換爲函數。另一方面,這個:

addUhum(_) 

是一個匿名函數。實際上,它是一個部分函數應用程序,因爲這個參數沒有被應用,整個事物被轉換成一個函數。它擴展爲:

x => addUhum(x) 

擴張的確切規則有點難以解釋,但是,基本上,該函數將「開始」,在最裏面的表達式分隔符。例外是部分功能應用程序,其中「x」移到功能外 - 如果使用_代替參數。

無論如何,這是它是如何擴展:

val ummThenAhem = x => addAhem(x).compose(y => addUmm(y)) 

唉,類型inferencer不知道X或Y的類型。如果您願意,您可以使用參數-Ytyper-debug查看它所嘗試的內容。