程序员

Functor函子

2021-01-04  本文已影响0人  翔子丶
为什么学习函子?

函子是函数式编程里面最重要的数据类型,也是基本的运算单位和功能单位

函子作用:控制函数式编程中的副作用

概念
Functor函子

特点:

// 一个容器 包裹一个值
class Container {
  // of静态方法,可以省略new关键字创建对象 因为new命令是面向对象编程的标志
  static of(value) {
    return new Container(value)
  }

  constructor(value) {
    this._value = value
  }

  // map方法,传入变形函数,将容器里的每一个值映射到另一个容器
  map(fn) {
    return Container.of(fn(this._value))
  }
}

let r = Container.of(5)
  .map((x) => x + 2)
  .map((x) => x * x)

console.log(r) // Container { _value: 49 }

// 演示 null undefined 的问题 值如果不小心传入了空值(副作用)
Container.of(null).map((x) => x.toUpperCase()) // TypeError: Cannot read property 'toUpperCase' of null
MayBe函子
// MayBe 函子
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
  }
}

// 传入具体值
let r = MayBe.of('Hello World').map((x) => x.toUpperCase())
console.log(r) // MayBe { _value: 'HELLO WORLD' }

// 传入null的情况
let r1 = MayBe.of(null).map((x) => x.toUpperCase())
console.log(r1) // MayBe { _value: null }

// 在MayBe函子中 很难确认是哪一步产生的空值问题
let r2 = MayBe.of('hello world')
  .map((x) => x.toUpperCase())
  .map((x) => null)
  .map((x) => x.split(' '))
console.log(r2) // MayBe { _value: null }
Either函子
// Either函子内部有Left(左值)和Right(右值) 右值是正常情况下使用 左值是右值不存在时使用
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))
    }
}
// either函子用来处理异常
function parseJson (str) {
    try {
        return Right.of(JSON.parsr(str))
    } catch (e) {
        return Left.iof({ error: e.message })
    }
}
let r = parseJSON('{ name: zs }')
console.log(r) // Left { _value: { error: 'Unexpected token n in JSON at position 2' } }
let r1 = parseJSON('{ "name": "zs" }')
          .map(x => x.name.toUpperCase())
console.log(r1) // Right { _value: 'ZS' }
IO函子
// IO 函子
const fp = require('lodash/fp')

class IO {
  static of(value) {
    return new IO(function () {
      return value
    })
  }

  constructor(fn) {
    this._value = fn
  }
  // 把当前的 value 和 传入的 fn 组合成一个新的函数
  map(fn) {
    return new IO(fp.flowRight(fn, this._value))
  }
}

let r = IO.of(process).map((p) => p.execPath)
console.log(r._value())

const fs = require('fs')
// 读取文件本是不纯的操作 但是readFile确实纯函数 因为总是返回IO函子
const readFile = (filename) => fs.readFileSync(filename, 'utf-8')
let r1 = IO.of(readFile('./01-functor.js'))
console.log(r1._value())
Task异步执行

异步任务实现过于复杂,使用folktale中的Task演示

// Task 处理异步任务
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)
    })
  })
}

readFile('package.json')
  .map(split('\n'))
  .map(find(x => x.includes('version')))
  .run()
  .listen({
    onRejected: err => {
      console.log(err)
    },
    onResolved: value => {
      console.log(value)
    }
  })
ap函子

apapplicative(应用)的缩写,凡是部署了ap方法的函子,就是ap函子

ap函子实现函子的链式操作

// 一个容器 包裹一个值
const MayBe = require('./02-maybe')
class Ap {
  // of静态方法,可以省略new关键字创建对象
  static of(value) {
    return new Ap(value)
  }
  // ap方法的参数不是函数 而是另一个函子
  ap(F) {
    return Ap.of(this._value(F._value))
  }
  constructor(value) {
    this._value = value
  }

  // map方法,传入变形函数,将容器里的每一个值映射到另一个容器
  map(fn) {
    return Ap.of(fn(this._value))
  }
}

function add(x) {
  return function (y) {
    return x + y
  }
}
const x = Ap.of(add).ap(MayBe.of(2)).ap(MayBe.of(3))
console.log(x) // Ap { _value: 5 }
Monad(单子)

函子之中再包含函子的情况下会出现多层嵌套的函子,如IO(IO(x))

Monad函子

// IO Monad
const fs = require('fs')
const fp = require('lodash/fp')

class IO {
  static of (value) {
    return new IO(function () {
      return value
    })
  }

  constructor (fn) {
    this._value = fn
  }

  map (fn) {
    return new IO(fp.flowRight(fn, this._value))
  }

  join () {
    return this._value()
  }

  flatMap (fn) {
    return this.map(fn).join()
  }
}

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 r = readFile('package.json')
          // .map(x => x.toUpperCase())
          .map(fp.toUpper)
          .flatMap(print)
          .join()
console.log(r)
上一篇下一篇

猜你喜欢

热点阅读