Curried Function:到拿到所有需要的參數前… 一直回傳新函數的函數。
1 2 3 4 5 6 7 8 9 10
| var add = function(x) { return function(y) { return x + y; } }
var add3 = add(3); add3(4);
add(3)(4);
|
autoCurry in Wu.js will save us
1 2 3 4 5 6 7 8
| var add = function(x, y) { return x + y; }.autoCurry();
var add3 = add(3); add3(4)
add(3,5)
|
但我們為什麼需要 curry?參考下面這個組成新 function getTheOdds 的例子。 有了currying,我們可以透過給予不同參數來建立新的函數。
1 2 3 4 5 6 7 8
| var filter = function(f, xs) { return xs.filter(f); }
filter(isOdd, [1,2,3,4,5])
var getTheOdds = filter(isOdd); getTheOdds([1,2,3,4,5])
|
再來一個用loadash的酷例子
1 2 3 4 5 6 7 8 9 10 11 12
| var firstTwoLetters = function(words){ return _.map(words, function(word){ return _.first(word, 2); }); }
var firstTwoLetters = _.map(_.first(2));
_.map(_.first(2), ['jim', 'kate'])
|
=> Underscore.js的參數排列法讓currying變得不可能 總結currying的優點有下面四個:
* 一般化函數、要傳的變數名消失了
* 透過給不同參數就可以生成不同的函數
* 更簡潔的定義
* 讓函式的組合/合成 (composition) 變的可能
組合/合成 (composition):用多個函數來組成新函數 簡單的例子,用 first() 和 reverse() 來合成 last 函數
1 2 3 4 5 6 7 8
| var last = function(xs) { var sx = reverse(xs); return first(sx); }
var last = compose(first, reverse);
last([1,2,3])
|
另一個例子,chain backwardly
1 2 3 4 5 6 7
| var wordCount = function(str){ var words = split(' ', str); return length(words); }
var wordCount = compose(length, split(' ')); wordCount("There is a way to save the world")
|
Category Theory: 多個函數組合(compose),作用域互相對應的理論。Connecting the dot. 總結組合:
* 能從其他函數組成新函數
* 組合過程中把參數藏起來
* 極為高階的寫程式
* 有數學理論在後面支持
Functors map 打開了後面的 object 然後做一些事、再放回 object
1 2 3 4 5
| var plus1 = function(x){ return x + 1 }
plus1([3])
map(plus1, [3])
|
剛剛舉的例子,map 只能操作 array object、但下面試圖用 map 操作所有 object
1 2 3 4 5 6 7 8 9
| map(plus1, MyObject(3))
MyObject = function(val) { this.val = val; }
MyObject.prototype.map = function(f) { return MyObject(f(this.val)); }
|
如果對 object 定義了 map function,它就變成 functor null check的例子、Dynamic Safety:
1 2 3 4 5 6 7 8 9 10 11
| map(plus1, Maybe(3))
map(plus1, Maybe(null))
Maybe = function(val) { this.val = val; }
Maybe.prototype.map = function(f){ return this.val ? Maybe(f(this.val)) : Maybe(null); }
|
把 ES6 promise 變 functor 的例子
1 2 3 4 5 6 7 8 9
| map(populateTable, $.ajax.get('/posts');
Promise.prototype.map = function(f) { var promise = new Promise(); this.then(function(response){ promise.resolve(f(response)); }); return promise; }
|
再來一個和 html 合作的例子:對有和沒有 user_login 的情況下,更新歡迎頁面。
1 2 3 4 5 6 7 8
| $div = $("#myDiv");
var getGreeting = compose(concat('Welcome '), dot('name'));
var updateGreetingHtml = compose($div.html, getGreeting);
map(updateGreetingHtml, Maybe(App.current_user));
|
underscore 不讓人 extend map 總結 functor 能:
- 改變函數的行為卻不用變動 open/closed principle
- 不光只有 map, 還有 reduce & compose *
直覺且非私人的 api
- free formulas
- 動態型別安全/檢查
總結:underscore 能變得更加 functional。希望有更 functional 的 library