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