一 函子 Functor

  1. 函子就是个容器,他有个值并且有个map方法,map方法可以运行一个函数对值进行处理;如何把副作用控制在可控的范围内、异常操作、异步操作等可能需要用到函子;
  1. // 函子
  2. // class Container {
  3. // constructor(value) {
  4. // this._value = value;
  5. // }
  6. // map(fn) {
  7. // return new Container(fn(this._value));
  8. // }
  9. // }
  10. // const a = new Container(5)
  11. // .map( x => x + 1)
  12. // .map( x => x * x);
  13. // console.log(a);
  14. class Container {
  15. static of (value) {
  16. return new Container(value)
  17. }
  18. constructor(value) {
  19. this._value = value;
  20. }
  21. map(fn) {
  22. return new Container(fn(this._value));
  23. }
  24. }
  25. console.log(Container.of(5).map(x => x + 1).map( x => x * x))

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

二 mayBe函子

处理异常操作

  1. class Maybe {
  2. static of (value) {
  3. return new Maybe(value)
  4. }
  5. constructor(value) {
  6. this._value = value;
  7. }
  8. map(fn) {
  9. return this.isNothing() ? Maybe.of(null): Maybe.of(fn(this._value))
  10. }
  11. isNothing() {
  12. return this._value === null || this._value === undefined;
  13. }
  14. }
  15. // console.log(Maybe.of('hello world').map(x => x.toUpperCase()));
  16. // 可以处理空值
  17. // const r = Maybe.of(null).map(x => x.toUpperCase());
  18. // 发生在哪一步不知道
  19. const r = Maybe.of(null)
  20. .map(x => x.toUpperCase())
  21. .map(x => null)
  22. .map(x => x.split(' '));
  23. console.log(r)

三 either函子

两者中的一个,类似于if…else…的处理,异常会让函数变的不纯,either函子可以用来做异常处理;

  1. class Container {
  2. static of (value) {
  3. return new Container(value)
  4. }
  5. constructor(value) {
  6. this._value = value;
  7. }
  8. map(fn) {
  9. return Container.of(fn(this._value));
  10. }
  11. }
  12. class Left extends Container{
  13. map(fn) {
  14. return this
  15. }
  16. }
  17. class Right extends Container{
  18. }
  19. function parseJSON (str) {
  20. try{
  21. return Right.of(JSON.parse(str))
  22. } catch(error) {
  23. return Left.of({error: error.message})
  24. }
  25. }
  26. // const r1 = Right.of(12)
  27. // .map(x => x + 2);
  28. // const r2 = Left.of(12)
  29. // .map(x => x + 2);
  30. // const r3 = parseJSON('{name: zhangSan}')
  31. const r4 = parseJSON('{"name": "zhangSan"}')
  32. .map(x => x.name.toUpperCase())
  33. // console.log(r1)
  34. // console.log(r2)
  35. // console.log(r3)
  36. console.log(r4)

四 IO函子

  1. IO函子中的_value是个函数,这里把函数当作值处理;IO函子可以把不纯的动作存储到_value中,延迟执行这个不纯的操作(惰性行为),包装当前的操作;把不纯的操作当作调用者来处理;
  1. const fp = require('loadsh/fp');
  2. class IO {
  3. static of(value) {
  4. return new IO(function() {
  5. return value;
  6. });
  7. }
  8. constructor(fn) {
  9. this._value = fn;
  10. }
  11. map (fn) {
  12. return new IO(fp.flowRight(fn, this._value))
  13. }
  14. }
  15. // 调用
  16. let r = IO.of(process).map( p => p.execPath);
  17. // console.log(r);
  18. console.log(r._value());

五 folktale

一个标准的函数式编程库,异于lodash、ramda,他没有提供很多功能函数;只提供了函数式的操作,如compose、curry等,一些函子Task、Eithger、Maybe等;
异步任务的实现过于复杂,用folktale中的task来演示

  1. // folktale 中的 curry/compose函数
  2. const {compose, curry} = require ('folktale/core/lambda');
  3. const {toUpper, first} = require('lodash/fp');
  4. // let f = curry(2, (x, y) => x + y);
  5. // console.log(f(2,3));
  6. // console.log(f(2)(3))
  7. const f = compose(toUpper, first);
  8. console.log(f(['one', 'two']))

folktale task异步执行 folktale2.和folktal1.中的task区别很大,1.0中的用法更接近我们现在演示的函子

  1. const fs = require('fs');
  2. const {task} = require('folktale/concurrency/task');
  3. const {split, find} = require('loadsh/fp');
  4. function readFile(fileName) {
  5. return task(resolver => {
  6. fs.readFile(fileName, 'utf-8', (err, data) => {
  7. if(err) resolver.reject(err)
  8. resolver.resolve(data)
  9. })
  10. })
  11. }
  12. readFile('package.json')
  13. .map(split('\n'))
  14. .map(find(x => x.includes('version')))
  15. .run()
  16. .listen({
  17. onRejected: err => {
  18. console.log(err)
  19. },
  20. onResolved: data => {
  21. console.log(data)
  22. }
  23. })

六 pointed函子

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

七 io函子的问题

  1. const fp = require('loadsh/fp');
  2. const fs = require('fs');
  3. class IO {
  4. static of(value) {
  5. return new IO(function() {
  6. return value;
  7. });
  8. }
  9. constructor(fn) {
  10. this._value = fn;
  11. }
  12. map (fn) {
  13. return new IO(fp.flowRight(fn, this._value))
  14. }
  15. }
  16. const readFile = function(fileName) {
  17. return new IO(function(){
  18. return fs.readFileSync(fileName, 'utf-8')
  19. });
  20. }
  21. const print = function(x) {
  22. return new IO(function(){
  23. console.log(x);
  24. return x;
  25. })
  26. }
  27. const cat = fp.flowRight(print, readFile);
  28. const r = cat('package.json')._value()._value();
  29. console.log(r)

八 monad 函子

可以变扁的poinged函子。变扁就是解决函数嵌套的问题;一个函子如果具有join和of的两个方法并遵守一些定律就是monad;

  1. const fp = require('loadsh/fp');
  2. const fs = require('fs');
  3. class IO {
  4. static of(value) {
  5. return new IO(function() {
  6. return value;
  7. });
  8. }
  9. constructor(fn) {
  10. this._value = fn;
  11. }
  12. map (fn) {
  13. return new IO(fp.flowRight(fn, this._value))
  14. }
  15. join() {
  16. return this._value()
  17. }
  18. flatMap(fn) {
  19. return this.map(fn).join();
  20. }
  21. }
  22. const readFile = function(fileName) {
  23. return new IO(function(){
  24. return fs.readFileSync(fileName, 'utf-8')
  25. });
  26. }
  27. const print = function(x) {
  28. return new IO(function(){
  29. console.log(x);
  30. return x;
  31. })
  32. }
  33. const r = readFile('package.json')
  34. .map(fp.toUpper)
  35. .flatMap(print)
  36. .join()
  37. console.log(r)

九 函子的练习

support.js

  1. class Container {
  2. static of(value) {
  3. return new Container(value);
  4. }
  5. constructor(value) {
  6. this._value = value;
  7. }
  8. map(fn) {
  9. return Container.of(fn(this._value));
  10. }
  11. }
  12. class MayBe {
  13. static of(x) {
  14. return new MayBe(x);
  15. }
  16. constructor(x) {
  17. this._value = x;
  18. }
  19. isNothing() {
  20. return this._value === null || this._value === undefined;
  21. }
  22. map(fn) {
  23. return this.isNothing() ? this : MayBe.of(fn(this._value));
  24. }
  25. }
  26. module.exports = { MayBe, Container };
  1. const fp = require('loadsh/fp');
  2. const {MayBe, Container} = require('./support');
  3. // 使用fp.add 和 fp.map创建一个能让functor里的值增加的函数
  4. let mayBe = MayBe.of([5, 6, 1]);
  5. let ex1 = () => {
  6. return mayBe.map(fp.map( item => fp.add(item, item)))
  7. }
  8. console.log(ex1())
  9. // 使用一个函数ex2 能够使用fp.first获取列表的第一个元素
  10. let xs = Container.of(['do','ray','me','fa','so','la','ti','do']);
  11. let ex2 = () => {
  12. return xs.map(fp.first)
  13. }
  14. console.log(ex2())
  15. // 实现一个函数ex3, 使用 safeProp 和 fp.first 找到user 名字的首字母
  16. let safeProp = fp.curry(function(x, o) {
  17. return MayBe.of(o[x])
  18. });
  19. let user = {id: 2, name: 'Albert'};
  20. let ex3 = () => {
  21. return safeProp('name', user).map(fp.first);
  22. }
  23. console.log(ex3())
  24. // 使用 Maybe 重写ex4,不要有if语句
  25. // let ex4 = function(n) {
  26. // if(n) return parseInt(n);
  27. // }
  28. let ex4 = (n) => {
  29. return MayBe.of(n).map(parseInt)
  30. }