函子 (Functor)

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

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

它首先是一种范畴,也就是说,是一个容器,包含了值和变形关系。比较特殊的是,它的变形关系可以依次作用于每一个值,将当前容器变形成另一个容器。

上图中,左侧的圆圈就是一个函子,表示人名的范畴。外部传入函数f,会转成右边表示早餐的范畴。

下面是一张更一般的图。

上图中,函数f完成值的转换(a到b),将它传入函子,就可以实现范畴的转换(Fa到Fb)。

容器

我们创建一个容器(container),这个容器必须能够装载任意类型的值。

函数式编程一般约定,函子有一个of方法,用来生成新的容器,我们将使用 Container.of 作为构造器(constructor),这样就省去了糟糕的面向对象的 new 的写法。

class Container {
  constructor(val) {
    this.val = val;
  }
  static of(x) {
    return new Container(x);
  };
  map(f) {
    return Container.of(f(this.val));
  }
}
// 使用容器
Container.of("hotdogs"); // Container { val: 'hotdogs' }

一旦容器里有了值,不管这个值是什么,我们就需要一种方法来让别的函数能够操作它,就是我们上面创建的 map 方法

Container.of(2).map(two => two + 2 ); // Container { val: 4 }
Container.of(2).map(two => two + 2 ).map(num => num * 2 ); // Container { val: 8 }

之后 Container 通常写为 Functor

class Functor {
  constructor(val) {
    this.val = val;
  }
  static of(x) {
    return new Functor(x);
  };
  map(f) {
    return Functor.of(f(this.val));
  }
}

空值检测:Maybe 函子

Maybe 看起来跟 Container 非常类似,但是有一点不同:Maybe 会先检查自己的值是否为空,然后才调用传进来的函数。这样我们在使用 map 的时候就能避免恼人的空值了

const _ = require('ramda')
class Maybe {
  constructor(val) {
    this.val = val;
  }
  static of(x) {
    return new Maybe(x);
  };
  isNothing() {
    return (this.val === null || this.val === undefined);
  }
  map(f) {
    return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.val));
  }
}
Maybe.of("Malkovich Malkovich").map(_.match(/a/ig));
//=> Maybe(['a', 'a'])
Maybe.of(null).map(_.match(/a/ig));
//=> Maybe(null)
Maybe.of({name: "Boris"}).map(_.prop("age")).map(_.add(10));
//=> Maybe(null)
Maybe.of({name: "Dinah", age: 14}).map(_.prop("age")).map(_.add(10));
//=> Maybe(24)

注意看,当传给 map 的值是 null 时,代码并没有报出错误。这是因为每一次 Maybe 要调用函数的时候,都会先检查它自己的值是否为空。

释放容器里的值

“如果一个程序运行之后没有可观察到的作用,那它到底运行了没有?”。或者,运行之后达到自身的目的了没有?有可能它只是浪费了几个 CPU 周期然后就去睡觉了...

应用程序所做的工作就是获取、更改和保存数据直到不再需要它们,对数据做这些操作的函数有可能被 map 调用,这样的话数据就可以不用离开它温暖舒适的容器。

不过,对容器里的值来说,还是有个逃生口可以出去。

Maybe.prototype.getVal = function() {
  return this.val
}
Maybe.of(1).map(val => val + 2).map(val => val * 2).getVal() // 6

MIT Licensed | Copyright © 2018-present 滇ICP备16006294号

Design by Quanzaiyu | Power by VuePress