在范畴论中,函子是范畴间的一类映射。函子也可以解释为小范畴范畴内的态射。
函子首先现身于代数拓扑学,其中拓扑空间的连续映射给出相应的代数对象(如基本群、同调群或上同调群)的代数同态。在当代数学中,函子被用来描述各种范畴间的关系。
函子(Functor)是函数式编程中的一个重要概念,源自于范畴论。在fp范式编程语言中,函子通常指的是一种可以被映射(map)的数据结构。它核心思想是,它提供了一种方式来对容器内的值进行操作,而不改变容器本身。
函子的基本特性:
映射(Mapping):函子提供了一个 map 方法,该方法接受一个函数作为参数,并将这个函数应用到容器内的每个元素上。
保持结构:映射操作不会改变容器本身的结构,只会改变容器内的值。
下面我们用js写一个简单的例子
jsclass Functor {
constructor(value) {
this.value = value;
}
map(fn) {
return new Functor(fn(this.value));
}
}
const f = new Functor(1);
const result = f.map(x => x + 1);
console.log(result.value); // 输出 2
map返回了一个新的Functor对象,既没有改变原来的对象结构,又完成了范畴间的转换。
现在我们初步了解了什么是函子,总的来说,他有一个map方法,参数是一个纯函数,用于对自身内元素(所有)应用这个纯函数。返回一个新的Functor,而不改变原来的对象。接下来我们来进阶一下。
以下是一些常见的函子
首先是Maybe函子,当Funtor的value是null的时候,map操作无疑是会失败的,会有空指针的问题。
Maybe 函子用于表示一个值可能存在或不存在的情况。它通常有两个子类型:Just 和 Nothing
Just:表示值存在的情况。
Nothing:表示值不存在的情况。
代码实现如下
jsclass Maybe {
constructor(value) {
this.value = value;
}
static just(value) {
return new Just(value);
}
static nothing() {
return new Nothing();
}
map(fn) {
return this.value ? Maybe.just(fn(this.value)) : Maybe.nothing();
}
}
class Just extends Maybe {
constructor(value) {
super(value);
}
}
class Nothing extends Maybe {
constructor() {
super(null);
}
}
const maybeValue = Maybe.just(5);
const result = maybeValue.map(x => x * 2);
console.log(result instanceof Just ? result.value : result); // 输出 10
const maybeNothing = Maybe.nothing();
const resultNothing = maybeNothing.map(x => x * 2);
console.log(resultNothing instanceof Nothing ? 'Nothing' : resultNothing.value); // 输出 'Nothing'
还有一种常见的函子是Either函子,Either 函子用于表示两种可能的情况:一种是“右值”(通常表示成功的结果),另一种是“左值”(通常表示错误或失败的原因)。
Right:表示成功的结果。
Left:表示错误或失败的原因。
Either 函子的一个重要特性是它可以明确地表示错误信息,而不仅仅是简单的“值不存在”。所以,Rust的Result和Either函子是等价的,而另一个最近新兴语言特性Optional,则更接近于Maybe函子,处理一个值为null和非null的情况,如Java,C++,Python都有相关实现。
jsclass Either {
constructor(value) {
this.value = value;
}
static right(value) {
return new Right(value);
}
static left(value) {
return new Left(value);
}
map(fn) {
return this instanceof Right ? Either.right(fn(this.value)) : Either.left(this.value);
}
}
class Right extends Either {
constructor(value) {
super(value);
}
}
class Left extends Either {
constructor(value) {
super(value);
}
}
const eitherRight = Either.right(5);
const resultRight = eitherRight.map(x => x * 2);
console.log(resultRight instanceof Right ? resultRight.value : resultRight.value); // 输出 10
const eitherLeft = Either.left('Error');
const resultLeft = eitherLeft.map(x => x * 2);
console.log(resultLeft instanceof Left ? resultLeft.value : resultLeft.value); // 输出 'Error'
还有更多的函子,如IO函子,Reader函子,List函子,可以自行了解。
函子的复合也是函子的重要概念,它允许我们将多个函子组合在一起,形成一个新的函子。这种组合方式类似于函数的复合,即一个函数的输出作为另一个函数的输入。
函子的复合概念
假设我们有两个函子 F 和 G,我们可以通过复合形成一个新的函子 F ∘ G(读作 "F after G" 或 "F composed with G")。这个新的函子将首先应用 G,然后将结果传递给 F。
示例:
jsclass Compose {
constructor(fg) {
this.fg = fg;
}
static of(fg) {
return new Compose(fg);
}
map(fn) {
return Compose.of(this.fg.map(g => g.map(fn)));
}
}
// 示例用法
class Maybe {
constructor(value) {
this.value = value;
}
static just(value) {
return new Maybe(value);
}
static nothing() {
return new Maybe(null);
}
map(fn) {
return this.value ? Maybe.just(fn(this.value)) : Maybe.nothing();
}
}
class List {
constructor(values) {
this.values = values;
}
static of(values) {
return new List(values);
}
map(fn) {
return List.of(this.values.map(fn));
}
}
const compose = Compose.of(List.of([Maybe.just(1), Maybe.just(2), Maybe.nothing()]));
const result = compose.map(x => x * 2);
console.log(result); // 输出: Compose { fg: List { values: [ Maybe { value: 2 }, Maybe { value: 4 }, Maybe { value: null } ] } }
函子复合允许我们定义一组函子,将他们组合成一个复杂的数据处理管道
本文作者:yowayimono
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!