成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

你造 Promise 就是 Monad 嗎

張率功 / 730人閱讀

摘要:但有時(shí)候,所有的鳥都會(huì)想要停在同一邊,皮爾斯就失去了平衡,就會(huì)讓他從鋼索上掉下去。我們這邊假設(shè)兩邊的鳥差異在三個(gè)之內(nèi)的時(shí)候,皮爾斯仍能保持平衡。

Monad 這個(gè)概念好難解釋, 你可以理解為一個(gè) Lazy 或者是狀態(tài)未知的盒子. 聽起來(lái)像是薛定諤貓(估計(jì)點(diǎn)進(jìn)去你會(huì)更暈了). 其實(shí)就是的, 在你打開這個(gè)盒子之前, 你是不知道里面的貓?zhí)幵谀欠N狀態(tài).

Monad 這個(gè)黑盒子, 里面到底賣的神馬藥,我們要打開喝了才知道.

等等, 不是說(shuō)好要解釋 Either 的嗎, 嗯嗯, 這里就是在解釋 Either. 上節(jié)說(shuō) Either 是一個(gè) Functor, 可以被 fmap over. 怎么這里又說(shuō)道黑盒子了? 好吧, Monad 其實(shí)也是 Functor. 還記得我說(shuō)的 Functor 其實(shí)是一個(gè)帶 context 的盒子嗎. 而 fmap 使得往盒子里應(yīng)用函數(shù)變換成為了可能.

Either

先來(lái)看看 Either 這種類型會(huì)干什么事情. Either 表示要不是左邊就是右邊的值, 因此我們可以用它來(lái)表示薛定諤貓, 要不是活著, 要不死了. Either 還有個(gè)方法:
either

(a -> c) -> (b -> c) -> Either a b -> c

想必你已經(jīng)對(duì)箭頭->非常熟了吧.如果前面幾章你都跳過(guò)了,我再翻譯下好了. 這里表示接收函數(shù)a->c和函數(shù) b->c, 再接收一個(gè) Either, 如果 Either 的值在左邊,則使用函數(shù)映射 a->c, 若值在右邊,則應(yīng)用第二個(gè)函數(shù)映射 b->c.

作為 Monad, 它還必須具備一個(gè)方法 ">>="(這個(gè)符號(hào)好眼熟的說(shuō), 看看 haskell 的 logo, 你就知道 Monad 是有多重要), 也就是 bind 方法.

bind 方法的意思很簡(jiǎn)單, 就是給這個(gè)盒子加一個(gè)操作, 比如往盒子在加放射性原子,如果貓活著,就是綠巨貓, 如果貓是死的,那就是綠巨死貓.

Left("cat").bind(cat => "hulk"+cat)
// => Left "hulkcat"
Right("deadcat").bind(cat => "hulk" + cat)
// => Right "hulkdeadcat"

這有個(gè)毛用啊. 表急... 來(lái)看個(gè)經(jīng)典例子

走鋼索

皮爾斯決定要辭掉他的工作改行試著走鋼索。他對(duì)走鋼索蠻在行的,不過(guò)仍有個(gè)小問(wèn)題。就是鳥會(huì)停在他拿的平衡竿上。他們會(huì)飛過(guò)來(lái)停一小會(huì)兒,然后再飛走。這樣的情況在兩邊的鳥的數(shù)量一樣時(shí)并不是個(gè)太大的問(wèn)題。但有時(shí)候,所有的鳥都會(huì)想要停在同一邊,皮爾斯就失去了平衡,就會(huì)讓他從鋼索上掉下去。

我們這邊假設(shè)兩邊的鳥差異在三個(gè)之內(nèi)的時(shí)候,皮爾斯仍能保持平衡。

一般解法

首先看看不用 Monad 怎么解

eweda.installTo(this);
var landLeft = eweda.curry(function(n, pole){
    return [pole[0]+n, pole[1]];
});
var landRight = eweda.curry(function(n, pole){
    return landLeft(n, eweda.reverse(pole));
});
var result = eweda.pipe(landLeft(1), landRight(1), landLeft(2))([0,0]);
console.log(result);
// => [3, 1]

還差一個(gè)判斷皮爾斯是否掉下來(lái)的操作.

var landLeft = eweda.curry(function(n, pole){
    if(pole==="dead") return pole;
    if(Math.abs(pole[0]-pole[1]) > 3)
      return "dead";
    return [pole[0]+n, pole[1]];
});
var landRight = eweda.curry(function(n, pole){
    if(pole==="dead") return pole;
    return landLeft(n, eweda.reverse(pole));
});
var result = eweda.pipe(landLeft(10), landRight(1), landRight(8))([0,0]);
console.log(result);
// => dead

完整代碼

現(xiàn)在來(lái)試試用 Either

我們先把皮爾斯放進(jìn) Either 盒子里, 這樣皮爾斯的狀態(tài)只有打開 Either 才能看見. 假設(shè) Either Right 是活著, Left 的話皮爾斯掛了.

var land = eweda.curry(function(lr, n, pole){
    pole[lr] = pole[lr] + n;
    if(Math.abs(pole[0]-pole[1]) > 3) {
      return new Left("dead when land " + n + " became " + pole);
    }
    return new Right(pole);
});

var landLeft = land(0)
var landRight = land(1);

現(xiàn)在落鳥后會(huì)返回一個(gè) Either, 要不活著, 要不掛了. 打開盒子的函數(shù)可以是這樣的

var stillAlive = function(x){
    console.log(x)
}
var dead = function(x){
    console.log("皮爾斯" + x);
}
either(dead, stillAlive, landLeft(2, [0,0]))

好吧, 好像有一點(diǎn)點(diǎn)像了, 但是這只落了一次鳥, 如果我要落好幾次呢. 這就需要實(shí)現(xiàn) Either 的 >>= bind 方法了, 如果你還記得前面實(shí)現(xiàn)的 Functor, 這里非常像 :

var Monad = function(type, defs) {
  for (name in defs){
    type.prototype[name] = defs[name];
  }
  return type;
};
function Left(value){
  this.value = value
}
function Right(value){
  this.value=value;
}

Monad(Right, {
  bind:function(fn){
    return fn(this.value)
  }
})

Monad(Left, {
  bind: function(fn){
    return this;
  }
})

哦, 對(duì)了, either:

either = function(left, right, either){
    if(either.constructor.name === "Right")
        return right(either.value)
    else
        return left(either.value)
}

我們來(lái)試試工作不工作.

var walkInLine = new Right([0,0]);
eitherDeadOrNot = walkInLine.bind(landLeft(2))
    .bind(landRight(5))
either(dead, stillAlive, eitherDeadOrNot)
// => [2,5]
eitherDeadOrNot = walkInLine.bind(landLeft(2))
  .bind(landRight(5))
  .bind(landLeft(3))
  .bind(landLeft(10)
  .bind(landRight(10)))

either(dead, stillAlive, eitherDeadOrNot)
// => "皮爾斯dead when land 10 became 15,5"

完整代碼

到底有什么用呢, Monad

我們來(lái)總結(jié)下兩種做法有什么區(qū)別:

一般做法每次都會(huì)檢查查爾斯掛了沒(méi)掛, 也就是重復(fù)獲得之前操作的 context

Monad 不對(duì)異常做處理, 只是不停地往盒子里加操作. 你可以看到對(duì)錯(cuò)誤的處理推到了最后取值的 either.

Monad 互相傳遞的只是盒子, 而一般寫法會(huì)把異常往下傳如"dead", 這樣導(dǎo)致后面的操作都得先判斷這個(gè)異常.

comment 由于是用 JavaScript, pole 不限定類型, 所以這里單純的用字符串代表 pole 的異常狀態(tài). 但如果換成強(qiáng)類型的 Java, 可能實(shí)現(xiàn)就沒(méi)這么簡(jiǎn)單了.

看來(lái)已經(jīng)優(yōu)勢(shì)已經(jīng)逐步明顯了呢, Monad 里面保留了值的 context, 也就是我們對(duì)這個(gè) Monad 可以集中在多帶帶的本次如何操作value, 而不用關(guān)心 context.

還有一個(gè) Monad 叫做 Maybe, 實(shí)際上皮爾斯的

Monad 在 JavaScript 中的應(yīng)用

你知道 ES6有個(gè)新的 類型 Promise 嗎, 如果不知道, 想必也聽過(guò) jQuery 的 $.ajax吧, 但如果你沒(méi)聽過(guò) promise, 說(shuō)明你沒(méi)有認(rèn)真看過(guò)他的返回值:

var aPromise = $.ajax({
    url: "https://api.github.com/users/jcouyang/gists"
    dataType: "jsonp"
    })
aPromise /***
=> Object { state: .Deferred/r.state(),
    always: .Deferred/r.always(),
    then: .Deferred/r.then(),
    promise: .Deferred/r.promise(),
    pipe: .Deferred/r.then(),
    done: b.Callbacks/p.add(),
    fail: b.Callbacks/p.add(),
    progress: b.Callbacks/p.add() }
***/

我們看到返回了好多Deferred類型的玩意, 我們來(lái)試試這玩意有什么用

anotherPromise = aPromise.then(_ => _.data.forEach(y=> console.log(y.description)))
/* =>
Object { state: .Deferred/r.state(),
    always: .Deferred/r.always(),
    then: .Deferred/r.then(),
    promise: .Deferred/r.promise(),
    pipe: .Deferred/r.then(),
    done: b.Callbacks/p.add(),
    fail: b.Callbacks/p.add(),
    progress: b.Callbacks/p.add() }

"connect cisco anyconnect in terminal"
"為什么要柯里化(curry)"
"批量獲取人人影視下載鏈接"
......
*/

看見沒(méi)有, 他又返回了同樣一個(gè)東西, 而且傳給 then 的函數(shù)可以操作這個(gè)對(duì)象里面的值. 這個(gè)對(duì)象其實(shí)就是 Promise 了. 為什么說(shuō)這是 Monad 呢, 來(lái)試試再寫一次走鋼絲:

這里我們用的是 ES6 的 Promise, 而不用 jQuery Defered, 記得用 firefox 哦. 另外 eweda 可以這樣裝

var ewd = document.createElement("script"); ewd.type = "text/javascript"; ewd.async = true;
            ewd.src = "https://rawgit.com/CrossEye/eweda/master/eweda.js";
(document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(ewd);

eweda.installTo(this); //安裝到 window 上
var land = curry(function(lr, n, pole){
    pole[lr] = pole[lr] + n;
    if(Math.abs(pole[0]-pole[1]) > 3) {
      return new Promise((resovle,reject)=>reject("dead when land " + n + " became " + pole));
    }
    return new Promise((resolve,reject)=>resolve(pole));
});

var landLeft = land(0)
var landRight = land(1);

Promise.all([0,0])
.then(landLeft(2), _=>_)
.then(landRight(3), _=>_) // => Array [ 2, 3 ]
.then(landLeft(10), _=>_)
.then(landRight(10), _=>_)
.then(_=>console.log(_),_=>console.log(_))
// => "dead when land 10 became 12,3"

這下是不承認(rèn) Promise 就是 Monad 了. 原來(lái)我們?cè)缫言谑褂眠@個(gè)神秘的 Monad, 再想想 Promise,也沒(méi)有那么抽象和神秘了.

ref: Functional JavaScript 第四章

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/87594.html

相關(guān)文章

  • 函數(shù)式JS: 原來(lái)promise是這樣的monad

    摘要:定義這是類型簽名的表述。實(shí)際上對(duì)應(yīng)著,只是在里作為立即量傳入,在和的返回值中作為閉包引用傳入。同時(shí)根據(jù)看出返回值是用回調(diào)返回值的。的輸出是的包裹。的方法借助了閉包引用額外輸入了,而輸入的函數(shù)輸入是輸出則是借助實(shí)現(xiàn)的。 轉(zhuǎn)載請(qǐng)注明出處: http://hai.li/2017/03/27/prom... 背景 上篇文章 函數(shù)式JS: 一種continuation monad推導(dǎo) 得到了一個(gè)...

    ZweiZhao 評(píng)論0 收藏0
  • Js-函數(shù)式編程

    摘要:組合組合的功能非常強(qiáng)大,也是函數(shù)式編程的一個(gè)核心概念,所謂的對(duì)過(guò)程進(jìn)行封裝很大程度上就是依賴于組合。在理解之前,先認(rèn)識(shí)一個(gè)東西概念容器容器為函數(shù)式編程里普通的變量對(duì)象函數(shù)提供了一層極其強(qiáng)大的外衣,賦予了它們一些很驚艷的特性。 前言 JavaScript是一門多范式語(yǔ)言,即可使用OOP(面向?qū)ο螅?,也可以使用FP(函數(shù)式),由于筆者最近在學(xué)習(xí)React相關(guān)的技術(shù)棧,想進(jìn)一步深入了解其思想...

    whinc 評(píng)論0 收藏0
  • 和少婦白潔一起學(xué)JavaScript之Async/Await

    摘要:匿名函數(shù)是我們喜歡的一個(gè)重要原因,也是,它們分別消除了很多代碼細(xì)節(jié)上需要命名變量名或函數(shù)名的需要。這個(gè)匿名函數(shù)內(nèi),有更多的操作,根據(jù)的結(jié)果針對(duì)目錄和文件做了不同處理,而且有遞歸。 能和微博上的 @響馬 (fibjs作者)掰扯這個(gè)問(wèn)題是我的榮幸。 事情緣起于知乎上的一個(gè)熱貼,諸神都發(fā)表了意見: https://www.zhihu.com/questio... 這一篇不是要說(shuō)明白什么是as...

    Bryan 評(píng)論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問(wèn)題描述 在開發(fā)過(guò)程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過(guò)http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過(guò)...

    tuniutech 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<