摘要:函子上面容器上定義了方法,的定義也類似是實(shí)現(xiàn)了函數(shù)并遵守一些特定規(guī)則的容器類型。不同類型的函子容器在處理內(nèi)部值時(shí),經(jīng)常遇到傳入?yún)?shù)異常的情況的情況,檢查值的合理性就非常重要。函子保證在調(diào)用傳入的函數(shù)之前,檢查值是否為空。
最近一直在學(xué)習(xí)函數(shù)式編程,前面介紹了函數(shù)式編程中非常重要的兩個(gè)運(yùn)算函數(shù)柯里化 和 函數(shù)組合,下文出現(xiàn)的curry 和 compose函數(shù)可以從前兩篇文章中找到。它們都可以直接在實(shí)際開發(fā)中用到,寫出函數(shù)式的程序。
本文主要初探容器的相關(guān)概念,以及如何處理編程中異步操作,錯(cuò)誤處理等依賴外部環(huán)境狀態(tài)變化的情況,,
容器(container)容器可以想象成一個(gè)瓶子,也就是一個(gè)對(duì)象,里面可以放各種不同類型的值。想想,瓶子還有一個(gè)特點(diǎn),跟外界隔開,只有從瓶口才能拿到里面的東西;類比看看, container 回暴露出接口供外界操作內(nèi)部的值。
一個(gè)典型的容器示例:
var Container = function(x) { this.__value = x; } Container.of = function(x) { return new Container(x); } Container.of("test") // 在chrome下會(huì)打印出 // Container?{__value: "test"}
我們已經(jīng)實(shí)現(xiàn)了一個(gè)容器,并且實(shí)現(xiàn)了一個(gè)把值放到容器里面的 Container.of方法,簡(jiǎn)單看,它像是一個(gè)利用工廠模式創(chuàng)建特定對(duì)象的方法。of方法正是返回一個(gè)container。
函子(functor)上面容器上定義了of方法,functor的定義也類似
Functor 是實(shí)現(xiàn)了map函數(shù)并遵守一些特定規(guī)則的容器類型。
把值留在容器中,只能暴露出map接口處理它。函子是非常重要的數(shù)據(jù)類型,后面會(huì)講到各種不同功能的函子,對(duì)應(yīng)處理各種依賴外部變量狀態(tài)的問題。
Container.prototype.map = function(f) { return Container.of(f(this.__value)) }
把即將處理容器內(nèi)變量的函數(shù),包裹在map方法里面,返回的執(zhí)行結(jié)果也會(huì)是一個(gè)Container。
這樣有幾點(diǎn)好處:
保證容器內(nèi)的value一直不會(huì)暴露出去,
對(duì)value的操作方法最終會(huì)交給容器執(zhí)行,可以決定何時(shí)執(zhí)行。
方便鏈?zhǔn)秸{(diào)用
// 利用上一篇中講到的柯里化,就可以看出其特性。 var add2 = function(x, y) { return x + y; }; curriedAdd = curry(add2); Container.of(2).map(curriedAdd(3)); // Container?{__value: 5}不同類型的函子 maybe
容器在處理內(nèi)部值時(shí),經(jīng)常遇到傳入?yún)?shù)異常的情況的情況,檢查value 值的合理性就非常重要。Maybe 函子保證在調(diào)用傳入的函數(shù)之前,檢查值是否為空。
var Maybe = function(x) { this.__value = x; } Maybe.of = function(x) { return new Maybe(x); } Maybe.prototype.isNothing = function() { return (this.__value === null || this.__value === undefined); } Maybe.prototype.map = function(f) { return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value)); }
這個(gè)通用的例子,體現(xiàn)了輸出結(jié)果的不確定性,也可以看出,在容器內(nèi)部所有對(duì)value值的操作,都會(huì)交給容器來執(zhí)行。在value 為空的情況下,也會(huì)返回包裹著null的容器,避免后續(xù)容器鏈?zhǔn)秸{(diào)用報(bào)錯(cuò)。
異常捕獲函子通常 使用throw/catch就可以捕獲異常,拋出錯(cuò)誤,但它并不是一種純函數(shù)方法,最好的方法是在出現(xiàn)異常時(shí),可以正常返回信息。Either函子,內(nèi)部?jī)蓚€(gè)子類Left 和 right; 可以看成右值是正常情況下使用并返回的值,左值是操作簡(jiǎn)單的默認(rèn)值。
var Left = function(x) { this.__value = x; } Left.of = function(x) { return new Left(x); } Left.prototype.map = function(f) { return this; } var Right = function(x) { this.__value = x; } Right.of = function(x) { return new Right(x); } Right.prototype.map = function(f) { return Right.of(f(this.__value)); } // 輸入數(shù)據(jù)進(jìn)行校驗(yàn) var setage = function(age) { return typeof age === "number"? Right.of(age): Left.of("error age") } setage(12).map(function(age){return "my age is" + age}) // Right?{__value: "my age is12"} setage("age").map(function(age){return "my age is" + age}) // Left?{__value: "error age"}
left 和 right 唯一的區(qū)別在于map 方法的實(shí)現(xiàn),當(dāng)然,一個(gè)函子最大的特點(diǎn)也體現(xiàn)在map方法上,
Left.map 不管傳入的是什么函數(shù),直接返回當(dāng)前容器;Right.map則是示例里面的方法一樣。
IO 操作本身就是不純的操作,生來就得跟外界環(huán)境變量打交道,不過可以掩蓋他的不確定性。跟下面localStorage包裹函數(shù)類似,延遲執(zhí)行IO 操作。
var getStorage = function(key) { return function() { return localStorage[key]; } }
再看看,封裝了高級(jí)一點(diǎn)的IO 函子:
var IO = function(f) { this.__value = f; } IO.of = function(x) { return new IO(function(){ return x; }) } IO.prototype.map = function(f) { // 使用上一句定義的compose函數(shù) return new IO(compose(f, this.__value)) }
compose函數(shù)組合,里面存放的都是函數(shù),this.__value跟其他函子內(nèi)部值不同,它是函數(shù)。IO.of方法在new對(duì)象之前,把值包裹在函數(shù)里面,試圖延遲執(zhí)行。
// 測(cè)試一下 var io__dom= new IO(function() {return window.document}) io__dom.map(function(doc) { return doc.title}) // IO?{__value: ?}
返回一個(gè)沒有執(zhí)行的函數(shù)對(duì)象,里面的__value值對(duì)應(yīng)的函數(shù),在上面函數(shù)調(diào)用后并沒有執(zhí)行,只有在調(diào)用了this.__value值后,才執(zhí)行。最后一步不純的操作,交給了函數(shù)調(diào)用者去做。
Monad一個(gè)functor, 只要他定義了一個(gè)join 方法和一個(gè)of 方法,那么它就是一個(gè)monad。 它可以將多層相同類型的嵌套扁平化,像剝洋蔥一樣。關(guān)鍵在于它比一般functor 多了一個(gè)join 方法。 我們先看看剝開一層的join方法。
var IO = function(f) { this.__value = f } IO.of = function(x) { return new IO(function(){ return x; }) } IO.prototype.join = function() { return this.__value ? this.__value(): IO.of(null); } // 包裹上兩層 var foo = IO.of(IO.of("test bar")); foo.join().__value(); // 返回里面嵌套著的IO類。 IO?{__value: ?},接著只需調(diào)用這里的__value(),就可以返回字符串`test bar`;
回頭看看前面map方法,return new IO(),生成新的容器,方便鏈?zhǔn)秸{(diào)用,跟 join方法結(jié)合一起使用,生成容器后,再扁平化。形成 chain 函數(shù),
var chain = curry(function(f, m) { return m.map(f).join(); })
看一個(gè)完整示例,其中curry 和compose,分別用到了鏈接里面的實(shí)現(xiàn),:
var IO = function(f) { this.__value = f; } IO.of = function(x) { return new IO(function() { return x; }) } IO.prototype.map = function(f) { // 使用上一句定義的compose函數(shù) return new IO(compose(f, this.__value)) } IO.prototype.join = function() { return this.__value ? this.__value() : IO.of(null); } var chain = curry(function(f, m) { return m.map(f).join(); }) var log = function(x) { return new IO(function() { console.log(x); return x; }) } var setStyle = curry(function(sel, props) { return new IO(function() { return document.querySelector(sel).style.background = props }) }) var getItem = function(key) { return new IO(function() { return localStorage.getItem(key); }) }; var map = curry(function(f, functor) { return functor.map(f); }); // 簡(jiǎn)單實(shí)現(xiàn)join var join = function(functor) { return functor.join(); } localStorage.background = "#000"; var setItemStyle = compose(join, map(setStyle("body")), join, map(log), getItem); // 換成 鏈?zhǔn)秸{(diào)用。 setItemStyle = compose(chain(setStyle("body")), chain(log), getItem); setItemStyle("background").__value(); // 操作dom 改變背景顏色總結(jié)
本文主要利用簡(jiǎn)單代碼舉例,介紹了容器,函子等相關(guān)概念,初步認(rèn)識(shí)了各種不同的函子。深入實(shí)踐示例,可以參考閱讀下面鏈接:
函數(shù)式編程風(fēng)格
js函數(shù)式編程指南https://llh911001.gitbooks.io...
JavaScript函數(shù)式編程(二)
JavaScript:函數(shù)式編程基本概念學(xué)習(xí)
JS函數(shù)式編程 - 函子和范疇論
javascript函數(shù)式編程之 函子(functor)
函數(shù)式編程入門教程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102475.html
摘要:前面我們已經(jīng)知道如何書寫函數(shù)式的程序了,但是我們還沒提到控制流異常處理異步操作和狀態(tài)呢容器容器為函數(shù)式編程里普通的變量對(duì)象函數(shù)提供了一層極其強(qiáng)大的外衣,賦予了它們一些很驚艷的特性按照我們的慣例,先從最簡(jiǎn)單的容器入手。 如果你前面都看完了跟到了這里,我只能說你很棒棒,不過我不得不說,這才剛剛開始。前面我們已經(jīng)知道如何書寫函數(shù)式的程序了,但是我們還沒提到控制流(control flow)、...
摘要:函數(shù)式編程二拖延癥了好久,第二篇終于寫出來了。如果你對(duì)熟悉的話應(yīng)該還記得,是可以調(diào)用來集中處理錯(cuò)誤的對(duì)于函數(shù)式編程我們也可以做同樣的操作,如果運(yùn)行正確,那么就返回正確的結(jié)果如果錯(cuò)誤,就返回一個(gè)用于描述錯(cuò)誤的結(jié)果。 JavaScript函數(shù)式編程(二) 拖延癥了好久,第二篇終于寫出來了。 上一篇在這里:JavaScript函數(shù)式編程(一) 上一篇文章里我們提到了純函數(shù)的概念,所謂的純函數(shù)...
摘要:組合組合的功能非常強(qiáng)大,也是函數(shù)式編程的一個(gè)核心概念,所謂的對(duì)過程進(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)一步深入了解其思想...
摘要:入門的導(dǎo)語(yǔ)廢話最近兩年你要說函數(shù)式編程不火的話那是不可能的是人都知道函數(shù)式編程很火為什么函數(shù)式編程會(huì)火呢在于它的思想很強(qiáng)大很強(qiáng)勢(shì)尤其是前端的更是在上完全使用純函數(shù)函數(shù)式的好處漸漸被發(fā)掘出來筆者最近看了一些函數(shù)式方面的東東現(xiàn)在發(fā)出來給大家學(xué)習(xí) 0x00 入門的導(dǎo)語(yǔ)(廢話) 最近兩年你要說函數(shù)式編程不火的話, 那是不可能的, 是人都知道函數(shù)式編程很火.為什么函數(shù)式編程會(huì)火呢, 在于它的思想...
摘要:函數(shù)式編程函數(shù)式,可能并不是那么難。在學(xué)習(xí)函數(shù)式編程之初,首先要知道在這一技能葉子中包含有多少個(gè)相關(guān)詞,其次要知道它和我們是否從未有過遇見。 JS函數(shù)式編程 函數(shù)式,可能并不是那么難。 在學(xué)習(xí)JS函數(shù)式編程之初,首先要知道在這一技能葉子中包含有多少個(gè)相關(guān)詞,其次要知道它和我們是否從未有過遇見。 一等公民、純函數(shù)、柯里化、代碼組合、pointfree、命令式與申明式、 Hindley...
閱讀 1682·2019-08-30 12:51
閱讀 670·2019-08-29 17:30
閱讀 3707·2019-08-29 15:17
閱讀 862·2019-08-28 18:10
閱讀 1373·2019-08-26 17:08
閱讀 2184·2019-08-26 12:16
閱讀 3446·2019-08-26 11:47
閱讀 3510·2019-08-23 16:18