[譯]淺入淺出Monads

pvmt9065 8年前發布 | 31K 次閱讀 Monad JavaScript開發工具包

大多數關于 monad 的教程都和老太太的裹腳布一樣,又臭、又長,說不清、道不明。當然我也不偉大,沒法保證我寫的一定更明了,更生動,甚至更屌?不過我至少可以確定,我這篇更簡潔。浪費不了你多少時間的!

廢話不多說,先看下面這個對象 Foo 。她就是個 monad 。你必定會吃驚道:我擦,這是什么意思?不要急,故事要從頭說,我們還是先來分析下 Foo 到底是怎么干活的:

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 的方法是可以被鏈式調用的:

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 實例的邏輯我們都可以抽離出去。

再來看看另一個 monad ,我們姑且稱之為 Bar 吧:

function Bar(value) {
  this.get = ()=> value;
  this.map = fn => {
      let result = fn(value);
      console.log(result);
      return new Bar(result);
  };
}

如果這時候我有一系列操作想順序作用在 value 上,而且還要在每次變化時打印出來新的 value ,就可以利用 Bar 把下面這種原始的,二逼的代碼:

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 可以粗略的歸納出下面這些規則:

  1. monad 總會包含一個值

  2. monad 有一個 map 方法,而且該方法會接受一個函數( handler )作為參數

  3. map 通過上一步提到的 handler 處理 value (還可能有些其他邏輯),并獲取其結果

  4. map 最后返回一個 new [Monad] ,以完成鏈式調用

目前能想到的就這些了。如果上述的例子你都理解了,那你就懂什么是 Monads 了。如果你還再等什么黑魔法或者驚奇算法,那抱歉了,還真沒有!

理論上,我們任意修改 map 的實現,任何可以在各步驟 handler 之間的邏輯都行 - 例如:決定傳什么內容到下一步,或者對上一步 handler 處理的結果做點兒什么。

空值檢查就是個不錯的例子:

function Maybe(value) {
  this.get = ()=> value;
  this.map = fn => {
      if (value === null) {
          return new Baz(null);
      } else {
          return new Baz(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 一樣遵守上面提到的規則。

掌握了這些技巧,你基本就可以裝做一個會寫函數式的“牛人”了!

原文地址: Monads Explained Quickly

來自: https://segmentfault.com/a/1190000004895315

 本文由用戶 pvmt9065 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!