打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
函子

Functor函子

定义:容器:包含值和值得变形关系(这个变形关系就是函数)
函子:是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系); 函子内部有一个值;对外还有一个方法map方法。

定义一个函子

class Container {
  // 定义一个静态方法 static of就是方法名  
  static of (value) {
    // 返回一个函子对象
    return new Container(value)
  }
  constructor (value) {
    // 函子内要公布一个值;这个值不对外公布
    this._value = value
  }
  // map方法时候接收一个处理值得纯函数。返回一个新的新函子来保存。
  map (fn) {
    return this.isNothing() ? Container.of(null) : Container.of(fn(this._value))
  }
  isNothing () {

    return this._value === null || this._value === undefined
  }
}
// 函子就是一个拥有map方法的一个对象
// 当创建一个函子对象的时候直接类名.调用of方法就可以了
let r = Container.of(5)
  .map(x => x + 1)
  .map(x => x * x)

console.log(r);

函子总结:函数式编程的运算不直接操作值,而是由函子完成。
函子就是一个实现了map锲约的对象
我们可以把函子想象成一个盒子,这个盒子内部封装了一个值
想要处理盒子中的值,我们需要给盒子的map方法传递一个处理值得函数(纯函数),由这个函数来对值进行处理。
最终map方法返回一个包含新值得函子

MayBe函子

就是对外部的空值情况做处理(控制副作用在允许的范围)

class MayBe {
  // 这个of方法可以减去在外部调用maybe函子的时候后还需要再次创建new MayBe()
  static of (value) {
    return new MayBe(value)
  }
  constructor (value) {
    this._value = value
  }
  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())
  .map(x => null)
  .map(x => x.split(' '))
console.log(r);

问题:MayBe函子可以避免空值错误;但是当我们多次调用map的时候;其中一个map出问题了;我们不知道问题出在哪里。

Either函子

定义:两者中的任何一个,类似于if…else…的处理。异常会让函数变得不纯,Either函子可以用来做异常处理。

class Left {
  static of (value) {
    return new Left(value)
  }
  constructor (value) {
    this._value = value
  }
  map (fn) {
    // 这种做法可以在left中嵌入一个错误消息
    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 (str) {
  // 如果发生异常不去处理的话就不是一个纯函数
  try {
    return Right.of(JSON.parse(str))
  }
  catch (e) {
    return Left.of({ error: e.message })
  }
}

// let r = parseJSON('{P:zd}')
// console.log(r);
let r = parseJSON('{'name':'zd'}').map(x => x.name.toUpperCase())

console.log(r);

io函子

io函子中的_value是一个函数,这里是吧函数作为值来处理;io函子可以把不纯的动作存储到_value中,延迟执行这个不纯的操作(惰性执行),包装当前的纯操作;把不纯的操作交给调用者来处理。

const fp = require('lodash/fp')

class IO {
  static of (x) {
    // console.log(x, '这个是x');

    return new IO(function () {
      return x
    })
  }

  constructor (fn) {
    this._value = fn
  }

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

// 案例  获取当前执行node的路径
let r = IO.of(process).map(p => p.execPath)
// console.log(r);
console.log(r._value());

console.log(111111, IO.of(process));

Monad函子

解决函子嵌套的方法:Monad函子:一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad

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

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

  constructor (fn) {
    this._value = fn
  }

  map (fn) {
    return new IO(fp.flowRight(fn, this._value))
  }
  join () {
    // 传递的函数最终也会返回一个函子
    return this._value()
  }
  flatMap (fn) {
    // map(fn)之后的函子中包裹的函数最终也会返回一个函子
    return this.map(fn).join()
  }
}

// 实现Linux中的cat方法(读取文件并打印)

let readFile = function (fileName) {
  return new IO(function () {
    // 同步读取文件readFileSync
    return fs.readFileSync(fileName, 'utf-8')
  })
}
// 打印
let print = function (x) {
  return new IO(function () {
    console.log(x, '这个是readFile返回来的函子');
    return x

  })
}
let cat = fp.flowRight(print, readFile)
// r 是IO(IO()) 函子嵌套  第一个_value()是打印的value,第二个是readFile的value
// let r = cat('../../package-lock.json')._value()._value()
// console.log(r);


// 解决函子嵌套的方法:Monad函子:一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad


// 当我们合并的函数返回的是值;就调用map;当我们要合并的函数返回的是一个函子;就调用flatmap
let r = readFile('../../package-lock.json')
  // .map(x => x.toUpperCase())
  .map(fp.toUpper)
  .flatMap(print)
  .join()

// 什么时候使用Monad:当一个函数返回一个函子的时候

Pointed函子

Pointed函子:是实现了of静态方法的函子
of 方法是为了避免使用new来创建对象,更深层的含义是of方法用来把值放到上下文Context(把值放到容器中,使用map来处理值。)

Task函子

// Task  处理异步任务
// node 里面读取文件用的是fs模块
const fs = require('fs')
const { task } = require('folktale/concurrency/task')
const { split, find } = require('lodash/fp')
// 读取文件的函数
function readFile (fileName) {
  return task(resolver => {
    // 异步读取文件 fs.readFile的参数有三个;第一个是文件的路径,第二个是编码格式,第三个是回调函数。在node里面是错误优先;所以回掉函数的参数是err,data
    fs.readFile(fileName, 'utf-8', (err, data) => {
      if (err) resolver.reject(err)
      resolver.resolve(data)
    })
  })
}

// 调用
// let task1 = readFile('../../package-lock.json')
// console.log(task1, '返回的是一个task函子');
// task函子必须要有run()方法;run方法就是去执行
readFile('../../package-lock.json')
  .map(split('\n'))
  .map(find(x => x.includes('version')))
  .run()
  .listen({
    onRejected: err => {
      console.log(err);
    },
    onResolved: data => {
      console.log(data);
    }
  })
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
13.Promise
数组中的map方法
JavaScript面试问题:函数式编程
如何手写Vue-next响应式呢?本文详解
函数之间可以相互调用
编程语言:类型系统的本质
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服