[譯]淺入淺出Monads

大多數關於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,還有一個牛逼的方法叫mapmap接受另外一個函數(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

  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 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,都有不一樣的用法和用途。但不論怎麼變化,她們也都和FooBar同樣遵照上面提到的規則。

掌握了這些技巧,你基本就能夠裝作一個會寫函數式的「牛人」了!

原文地址:Monads Explained Quickly

相關文章
相關標籤/搜索