函數(shù)式編程(四)

函子(Functor)

函子的作用,在函數(shù)式編程中如何把副作用控制在可控的范圍內(nèi)、異常處理、異步操作等。

什么是 Functor

  • 容器:包含值和值的變形關(guān)系(這個(gè)變形關(guān)系就是函數(shù))
  • 函子:是一個(gè)特殊的容器,通過一個(gè)普通的對(duì)象來實(shí)現(xiàn),該對(duì)象具有 map 方法,map 方法可以運(yùn)行一個(gè)函數(shù)對(duì)值進(jìn)行處理(變形關(guān)系)
    看概念總是云里霧里的,但還是得多讀幾遍,我們來自己實(shí)現(xiàn)一個(gè)函子。
// Functor 函子
class Container {
   constructor (value) {
     this._value = value // 函子中必須要有一個(gè)值,_開頭約定為內(nèi)部私有變量,不希望外部用到
   }
   map (fn) {
     return new Container(fn(this._value)) // 傳入的函數(shù)處理完此值后,需要再返回一個(gè)函子出去
   }
 }
// try
 let r = new Container(5) //return的是函子,能繼續(xù)鏈?zhǔn)秸{(diào)用
   .map(x => x + 1)
   .map(x => x * x)

 console.log(r) // => {_value: 36} 注意map,

上面就是一個(gè)最簡(jiǎn)單的函子, 我們要處理值value,并不是函子來處理,是我們向map里傳函數(shù)由他來處理。但是每次使用前還要new Container,不方便(更多的原因是因?yàn)楹瘮?shù)式編程,我們可不想看見new這樣的操作符,哈哈哈哈),于是

class Container {
  static of (value) { // new是避免不了了,就封裝一個(gè)of方法,在這里偷偷new
    return new Container(value) 
  }
  constructor (value) {
    this._value = value
  }
  map (fn) {
    return Container.of(fn(this._value)) //of是靜態(tài)方法,這里就可以直接用類名調(diào)用
  }
}
// try
 let r = Container.of(3)
   .map(x => x + 2)
   .map(x => x * x)

 console.log(r) // => {_value: 25} 

所以函子就是一個(gè)擁有map方法的對(duì)象,函子中要維護(hù)一個(gè)值(也就是你傳進(jìn)來的),這個(gè)值永遠(yuǎn)也不對(duì)外公布,就像這個(gè)值被包裹在一個(gè)盒子里面,想操作值就往map中傳函數(shù)就行了。

總結(jié)

  • 函數(shù)式編程的運(yùn)算不直接操作值,而是由函子完成
  • 函子就是一個(gè)實(shí)現(xiàn)了 map 契約的對(duì)象
  • 我們可以把函子想象成一個(gè)盒子,這個(gè)盒子里封裝了一個(gè)值想要處理盒子中的值,我們需要給盒子的 map 方法傳遞一個(gè)處理值的函數(shù)(純函數(shù)),由這
    個(gè)函數(shù)來對(duì)值進(jìn)行處理
  • 最終 map 方法返回一個(gè)包含新值的盒子(函子)

基本就是這樣,當(dāng)然它是不會(huì)這么簡(jiǎn)單的,我們用函子的目的就是用它來處理副作用

// 值如果不小心傳入了空值(副作用)
Container.of(null)
.map(x => x.toUpperCase())
// TypeError: Cannot read property 'toUpperCase' of null

MayBe 函子

我們?cè)诰幊痰倪^程中可能會(huì)遇到很多錯(cuò)誤,需要對(duì)這些錯(cuò)誤做相應(yīng)的處理
MayBe 函子的作用就是可以對(duì)外部的空值情況做處理(控制副作用在允許的范圍)

class MayBe {
  static of (value) {
    return new MayBe(value)
  }
  constructor (value) {
    this._value = value
  }
 isNothing () { // 這里新增一個(gè)判斷是否為空的函數(shù)
    return this._value === null || this._value === undefined
  }
  map (fn) { // 在執(zhí)行fn前判斷一下,為空時(shí)就返回一個(gè)null的函子
    return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
  }
}

相比以前加了個(gè)判斷而已,我們trytry

 let r = MayBe.of(null)
           .map(x => x.toUpperCase())
 console.log(r) // => {_value: null}  也沒沒有報(bào)錯(cuò)

// 他還是不夠完善,請(qǐng)看
MayBe.of('hello beijing')
.map(x => x.toUpperCase())
.map(x => null) // 假設(shè)這里出現(xiàn)了問題,產(chǎn)生了null傳遞了下去
.map(x => x.split(' '))
// =>  { _value: null }

雖然兜住了,但是Meybe很難追蹤到出現(xiàn)問題的地方,沒關(guān)系我們有Either 函子

Either 函子

  • Either 兩者中的任何一個(gè),類似于 if...else...的處理
  • 異常會(huì)讓函數(shù)變的不純,Either 函子可以用來做異常處理
// 這里我們需要兩個(gè)函子left right
class Left {
  static of (value) {
    return new Left(value)
  }
  constructor (value) {
    this._value = value
  }
  map (fn) {
    return this // 這里比較特殊,是直接返回這個(gè)對(duì)象,這里為何這樣下面解釋
  }
}
// 這個(gè)right跟以前的Container沒有區(qū)別
class Right {
    static of (value) {
        return new Right(value)
    }
    constructor (value) {
        this._value = value
    }
    map(fn) {
        return Right.of(fn(this._value))
    }
}
// try
// 用JSON.parse的異常來制造錯(cuò)誤
function parseJSON (str) {
  try {
    return Right.of(JSON.parse(str))
  } catch (e) {
    return Left.of({ error: e.message }) // 走到錯(cuò)誤里,就把錯(cuò)誤信息返給left,所以left的map直接return this就夠了
  }
}
let r = parseJSON('{ name: zs }') // 不給name加雙引號(hào),所以會(huì)報(bào)錯(cuò)
console.log(r)
// =>{_value: {error: "Unexpected token n in JSON at position 2"}}

沒有代碼報(bào)錯(cuò),且把異常信息通過函子展現(xiàn)出來了

=======分隔符======

之后還有IO函子,Monad函子等,這玩意我是真看不懂了,能力有限啊,我知道大家看到函子這里會(huì)很蒙,我也是,其實(shí)原因在于不知道在哪里應(yīng)用,不用擔(dān)心,我們平常用到的機(jī)會(huì)少之又少,包括面向?qū)ο缶幊?,我們用過么,我也就封裝過一些基礎(chǔ)的庫的時(shí)候用過,所以沒關(guān)系,慢慢來(安慰自己)
++++

除了最后剩的幾個(gè)函子外,函數(shù)式編程圓滿了!

附錄

函數(shù)式編程指北
函數(shù)式編程入門

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容