大多數關於monad
的教程都和老太太的裹腳布同樣,又臭、又長,說不清、道不明。固然我也不偉大,無法保證我寫的必定更明瞭,更生動,甚至更屌?不過我至少能夠肯定,我這篇更簡潔。浪費不了你多少時間的!javascript
廢話很少說,先看下面這個對象Foo
。她就是個monad
。你一定會吃驚道:我擦,這是什麼意思?不要急,故事要從頭說,咱們仍是先來分析下Foo
究竟是怎麼幹活的:java
function Foo(value) { this.get = ()=> value; this.map = fn => { let result = fn(value); return new Foo(result); }; }
Foo
接受了一個value
,並且一直都沒改變她的值。Foo
裏提供了一個get
方法使得外部調用者能夠獲取value
,還有一個牛逼的方法叫map
。map
接受另外一個函數(handler
)做爲參數,而後用接受的這個新函數handler
處理value
,將結果再次傳給Foo
,最後將實例化的新Foo
對象返回。算法
由於map
返回一個Foo
的實例,因而map
的方法是能夠被鏈式調用的:ide
let one = new Foo(1); let two = one.map(x => x + 7).map(x => x / 2).map(x => x - 2); two.get() === 2;
鏈式調用high不 high?她容許咱們能夠按照指望,對x
執行順序操做,這種更「天然」的風格絕對比下面這種瘋狂嵌套的風格要好:函數
//嵌套組合的方式長這個樣子, //咱們必須從右向左讀,才能得出結論 //並且你說實話,這風格,你喜歡麼? let two = minusTwo(divideByTwo(addSeven(1)));
並且每個步驟裏處理value
到下一個Foo
實例的邏輯咱們均可以抽離出去。ui
再來看看另外一個monad
,咱們姑且稱之爲Bar
吧:this
function Bar(value) { this.get = ()=> value; this.map = fn => { let result = fn(value); console.log(result); return new Bar(result); }; }
若是這時候我有一系列操做想順序做用在value
上,並且還要在每次變化時打印出來新的value
,就能夠利用Bar
把下面這種原始的,二逼的代碼:code
let stepOne = something(1); console.log(stepOne); let stepTwo = somethingElse(stepOne); console.log(stepTwo); let stepThree = somethingDifferent(stepTwo); console.log(stepThree);
重構成下面這種優雅的,高端的樣子了:對象
new Bar(1) .map(something) // console >> logs new value .map(somethingElse) // console >> logs new value .map(somethingDifferent); // console >> logs new value
如今你應該懂什麼是monads
了。我完成諾言了哦!Monads
能夠粗略的概括出下面這些規則:blog
monad
總會包含一個值
monad
有一個map
方法,並且該方法會接受一個函數(handler
)做爲參數
map
經過上一步提到的handler
處理value
(還可能有些其餘邏輯),並獲取其結果
map
最後返回一個new [Monad]
,以完成鏈式調用
目前能想到的就這些了。若是上述的例子你都理解了,那你就懂什麼是Monads
了。若是你還再等什麼黑魔法或者驚奇算法,那抱歉了,還真沒有!
理論上,咱們任意修改map
的實現,任何能夠在各步驟handler
之間的邏輯都行 - 例如:決定傳什麼內容到下一步,或者對上一步handler
處理的結果作點兒什麼。
空值檢查就是個不錯的例子:
function Maybe(value) { this.get = ()=> value; this.map = fn => { if (value === null) { return new Maybe(null); } else { return new Maybe(fn(value)); } }; }
這個實現裏,map
只在value
爲合法值(非空)時,傳入handler
。不然就只返回一個自身的copy。利用上面的非空檢查的Monad
函數Maybe
,咱們能夠把下面這個冗長矬的代碼:
let connection = getConnection(); let user = connection ? connection.getUser() : null; let address = user ? user.getAddress() : null; let zipCode = address ? address.getZip() : null;
重構成這個樣子:
let zipCode = new Maybe(getConnection()) .map(c => c.getUser()) .map(u => u.getAddress()) .map(a => a.getZip()); //最後獲得的要麼是真正的zipCode(每一步都正確處理) //要麼就是個null zipCode.get();
但願今天的說教已經說明了monads
和她的map
方法爲何這麼牛逼了!關於這點,我卻是不懷疑你也能本身想出來^^
還有其餘不少種monads
,都有不一樣的用法和用途。但不論怎麼變化,她們也都和Foo
、Bar
同樣遵照上面提到的規則。
掌握了這些技巧,你基本就能夠裝作一個會寫函數式的「牛人」了!