大前端

JavaScript 深度剖析---03函数式编程范式

2021-04-22  本文已影响0人  丽__
functor(涵子)

函数不仅可以用于同一个范畴之中值的转换,还可以用于将一个范畴转成另一个范畴。这就涉及到了函子(Functor)。

// // functor  函子
// class Container {
//     constructor(value) {
//         this._value = value
//     }
//     map(fn) {
//         return new Container(fn(this._value))
//     }
// }


// let r = new Container(5).map(x => x + 1).map(x => x * x)
// console.log(r);

上面代码中,Container 是一个函子,它的map方法接受函数f作为参数,然后返回一个新的函子,里面包含的值是被f处理过的(f(this.val))
一般约定,函子的标志就是容器具有map方法。该方法将容器里面的每一个值,映射到另一个容器。

函数式编程一般约定,函子有一个of方法,用来生成新的容器。

下面就用of方法替换掉new。

class Container {
    static of (value){
        return new Container(value)
    }
    constructor(value) {
        this._value = value
    }
    map(fn) {
        return Container.of(fn(this._value))
    }
}


let r = Container.of(5).map(x => x + 1).map(x => x * x)
console.log(r);

这就更像函数式编程了。

MayBe(函子)
Container.of(null).map(function (s) {
  return s.toUpperCase();
});
// TypeError

上面代码中,函子里面的值是null,结果小写变成大写的时候就出错了。

class MayBe {
    static of (value) {
        return new MayBe(value)
    }
    constructor(value) {
        this._value = value
    }
    // 如果对空值变形的话直接返回 值为 null 的函子
    map(fn) {
        return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
    }
    isNothing() {
        return this._value === null || this._value === undefined
    }
}

// 传入具体值 
MayBe.of('Hello World').map(x => x.toUpperCase())
// 传入 null 的情况 
MayBe.of(null).map(x => x.toUpperCase())
// => MayBe { _value: null }

Maybe 函子就是为了解决这一类问题而设计的。简单说,它的map方法里面设置了空值检查。

// 在 MayBe 函子中,我们很难确认是哪一步产生的空值问题,如下例:

MayBe.of('hello world').map(x => x.toUpperCase()).map(x => null).map(x => x.split(' ')) // => MayBe { _value: null }
//因此引入了Either函子
Either函子
class Left { 
  static of (value) { 
    return new Left(value) 
  }
  constructor (value) {
     this._value = value 
   }
   map (fn) { 
      return this 
    } 
}  

class Right { 
  static of (value) { 
    return new Right(value) 
  }
  constructor (value) { 
    this._value = value 
  }
  map(fn) {
     return Right.of(fn(this._value))
   }
}
function parseJSON(json) { 
  try { 
    return Right.of(JSON.parse(json));
  }catch (e) {
    return Left.of({ error: e.message}); 
  } 
}

let r = parseJSON('{ "name": "zs" }') .map(x => x.name.toUpperCase()) 
console.log(r)
IO 函子
const fp = require('lodash/fp') 
class IO { 
   static of (x) { 
      return new IO(function () {
        return x 
      })
    }
  constructor (fn) {
    this._value = fn 
  }
  map (fn) {
   // 把当前的 value 和 传入的 fn 组合成一个新的函数 
    return new IO(fp.flowRight(fn, this._value)) 
  }
}

// 调用

let io = IO.of(process).map(p => p.execPath) 
console.log(io._value())
Task 异步执行

异步任务的实现过于复杂,我们使用 folktale 中的 Task 来演示
folktale 一个标准的函数式编程库,和 lodash、ramda 不同的是,他没有提供很多功能函数,只提供了一些函数式处理的操作,例如:compose、curry 等,一些函子 Task、Either、 MayBe 等

const {compose,curry} = require('folktale/core/lambda')
const {toUpper,first} =require('lodash/fp')
//第一个参数传入函数的是参数个数
let f = curry(2,function(x,y){
  console.log(x+y);
})
f(3,4)
f(3)(4)

//函数组合
let f = compose(toUpper,first)
f(['one','two'])
const fs = require('fs')
const { task } = require('folktale/concurrency/task') 
const {split,find} = require('lodash/fp')
function readFile(filename) {
    return task(resolver => {
        fs.readFile(filename, 'utf-8', (err, data) => { 
            if (err) resolver.reject(err) 
            resolver.resolve(data) 
        }) 
      })
}
// 调用 run 执行
readFile('package.json') .map(split('\n')) 
  .map(find(x => x.includes('version')))
  .run().listen({ 
    onRejected: err => { 
        console.log(err) 
    },
    onResolved: value => { 
        console.log(value) 
    }
})
Pointed 函子
image.png image.png
class Container { 
    static of (value) {
       return new Container(value) }
      …… 
  }
Contanier.of(2) .map(x => x + 5)
Monad(单子)
const fs = require('fs') 
const fp = require('lodash/fp')

let readFile = function(filename){
  return new IO(function(){
    return fs.readFileSync(filename,'utf-8)
  })
}

let print = function(x){
  return new IO(function(){
    console.log(x);
    return x
  })
}

let cat = fp.flowRight(print,readFile)
let r = cat('package.json')._value()._value()

console.log(r);
上一篇 下一篇

猜你喜欢

热点阅读