本系列文章,內容以探討 Kyle Simpson. Functional-Light JavaScript 一書內容為主
- 目標:是讀懂 FP,能用 code 與人交流,而不是被壓在 FP 的術語大山下喘不過氣。
 - 原文地址:Functional-Light JavaScript
 
還記得昨天的 Partial Application 嗎?今天如果將函數改為一次傳一個參數,然後再次 return 另一個函數來處理下一個參數;
講的學術一點,將一個多參數函數拆解成一串連續的鏈式函數,每個小函數接受單一參數 Arity = 1 ,並 return 另一個函數接受下一個參數。
這種技巧,就叫 Currying (柯里化)
回想一下,我們的 ajax(url, data, cb),如果重構成 Curried  版本是這樣的:
curriedAjax(url)(data)(cb)
三個連續的 (..) 表示鏈式呼叫,也可以分拆成這樣:
var chain1 = curriedAjax(url)
var chain2 = chain1(data)
chain2(cb)
範例1
我們現在學到了,將一次接受所有參數的 ajax(..),改成
相同處:
每一次的 Curry,都將一個實參應用到原函數的形參,一直到傳入所有實參為止
相異處:
curriedAjax return 出的 chain1,是一個明確只接受下一個參數 data ,並非是接收所有剩下參數的函數。
回想 Day2,
// before currying
function sum (x, y, z) {
  return x + y + z
}
sum(2, 3, 1) // 5
// after currying 
var curried_sum = curry(sum)
curried_sum(2)(3)(1) // 6
Day2 範例1 currying 前後的 function call 差異
由此可以知道,Currying 就是把多參數(Arity > 1)函數轉化成一系列單參數(Arity = 1)的函數。
Day2 Currying 簡介 中的範例
function curry (fn, ARITY = fn.length) {
  return (function nextCurried (prevArgs) {
    return function curried (nextArg) {
      var args = [...prevArgs, nextArg]
      if (args.length >= ARITY) {
        return fn(...args)
      } else {
        return nextCurried(args)
      }
    }
  })( [] )
}
Day2 範例3 實作 curry function(二)
來改寫成 ES6 版本,
var curry = (fn, ARITY = fn.length, nextCurried) =>
    (nextCurried = prevArgs =>
        nextArg => {
            var args = [...prevArgs, nextArg]
            if (args.length >= ARITY) {
                return fn(...args)
            } else {
                return nextCurried(args)
            }    
        }
    )( [] )
範例4 curry 小工具 ES6 版本
[] 做為 prevArgs,收集已傳入的參數length 能明確表示函數形參個數,得知 Currying 需迭代的次數。...args,便要指定明確的參數個數傳入。function sum(...args) {
    var sum = 0;
    for (let i = 0; i < args.length; i++) {
        sum += args[i];
    }
    return sum;
}
sum( 1, 2, 3, 4, 5 );                        
// 運用 Currying
// 因為是不定長度參數 ...args,需指定個數
var curriedSum = curry( sum, 5 );
curriedSum( 1 )( 2 )( 3 )( 4 )( 5 );  // 15
範例5 不定參數 Currying
// 怕你忘記
function add (x, y) {
    return x + y
}
[30, 55, 42, 87, 66].map( partial( add, 10 ))
範例6 Day 10 全班加 10 分例子
由於 Partial Application 與 Currying 概念上相似,改成 Currying 方式來重構吧!
[30, 55, 42, 87, 66].map( curry( add )( 10 ) )
幾乎長得一樣呀?這樣說好了,假如一開始不知道要全班加幾分,可能題目有錯才會加,我先把 add 轉化成加 X 分的函數 addX
var addX = curry( add );
// 過一陣子...題目錯啦,加 3 分
[30, 55, 42, 87, 66].map( addX( 3 ) );
範例7
別只用看的,實際到 JSBIN 測試吧
在 JavaScript 裡,這兩個 FP 技巧都是運用 Closure 的概念實作的,保存部分參數,全部拿到後再執行,比較如下:
如果想要每次只 partially apply 一個也是可以的,不過讓 curry 一次幫你完成是更好的選擇。