Kotlin — Partial Application 與 Currying

Andy Lu
4 min read1 day ago

聊聊 Functional Programming

部分應用(Partial Application)

部分應用(Partial Application) 是 Functional Programming 的一種重要概念,可以減少函式的輸入參數,讓呼叫時減少輸入參數(少參數少變化)。

什麼時候會需要 Partial Application 呢?若有一個函式它需要輸入多個參數,在某些情境之下,有幾個參數是固定的,那麼我們就使用 Partial Application 的技巧將這些參數固定,進而產生一個只需要較少參數的函式。如此一來,我們就可以以更少的參數來使用這個函式。讓使用更靈活。

範例

函式 add 可以支援三個 Int 參數 a、b、c,如下:

fun add(a: Int, b: Int, c: Int): Int = a + b + c

將這三個整數傳入,就可以回傳出三個 Int 的總和。 如:

val sum = add(1, 2, 3)
//sum = 6

若使用 Partial Application 的方法將第一個參數固定,該如何操作呢?

fun partialAdd(a: Int): (Int, Int)-> Int =
{ b, c -> add(a, b, c) }

當我們要固定第一個 Int 參數為 100 時,我們可以這樣使用:

val add100 = partialAdd(100)
val result = add100(5, 10)
//result = 115

回過頭看 partialAdd 的簽名,它的輸入型別是 Int,輸出型別是 (Int,Int)->Int。 輸出型別是一個高階函數,其中函式的輸入為兩個 Int,輸出為 Int。

柯里化 (Currying)

前面介紹的 Partial Application 是將函式的參數減少,而接下來要介紹的柯里化(Curring)跟 Partial Application 有點相像,也是將所有的參數都成為一個單一參數的函式的過程。

我們期待能夠產出這樣的使用方式,每一個函式都能夠再帶入一個參數成為一個新的函數。

val add5 = add(5)
val add5And10 = add5(10) //add(5)(10)
val add5And10And20 = add5And10(20) //add(5)(10)(20)

如同 Partial Application,也是使用高階函數來定義: 在這邊,我們只需要定義 add() 函式即可

fun add(a: Int): (Int) -> (Int) -> Int = { b: Int -> { c: Int -> a + b + c } }

其中, add5(Int) -> (Int) -> Int 的函數, add5And10(Int) -> Int 的函數,最後再將 20 傳入就可以將 a, b, c 的值加起來。

如果嘗試去印出 add5, add5And10 的結果,可以發現得到的不是整數,而是一個 lambda,這是因為這兩個函數只是柯里化的過程。

結論

函數的參數越簡單、越少,能降低複雜度並提高可讀性,透過部分應用以及柯里化,可以讓函數的參數減少,減少複雜度。

部分應用與柯里化雖然都是將一個函式將其參數簡化的方法。但是其應用的場景不同。

部分應用並不一定會減少所有的參數,而是將部分參數提前固定。 柯里化則是將多參數函數轉換成一系列嵌套的單參數函數,能夠在每一層逐步組合其參數,更增加了靈活性。

雖然原生的函式庫並不支援這兩者,但是因為 Kotlin 支援 Functional Programming 的開發範式,能夠使用高階函數,我們可以自行利用高階函式、擴充函式自行設計滿足自身需求的部分應用以及柯里化。

--

--

Andy Lu

Android/Flutter developer, Kotlin Expert, like to learn and share.