代数数据类型
代数数据类型(Algebraic Data Types,ADT)是函数式编程中用于建模复杂数据结构的强大工具。
什么是代数数据类型?
代数数据类型由两种基本类型组合而成:
- 积类型(Product Types):包含多个值的类型(如元组、对象)
- 和类型(Sum Types):可以是多种类型之一的类型(如联合类型)
积类型
积类型表示同时包含多个值的类型。
// 元组 - 有序的值的集合
const point = [10, 20]; // [x, y]
const person = ['John', 25]; // [name, age]
// 对象 - 带标签的积类型
const user = {
name: 'John',
email: '[email protected]',
age: 25
};
和类型
和类型表示可以是多种可能之一的类型。
// 使用类实现和类型
class Shape {}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
}
// 使用函数处理不同的变体
const getArea = (shape) => {
if (shape instanceof Circle) {
return Math.PI * shape.radius ** 2;
} else if (shape instanceof Rectangle) {
return shape.width * shape.height;
}
throw new Error('Unknown shape');
};
Maybe 类型
Maybe 类型表示一个值可能存在也可能不存在。
class Maybe {
static just(value) {
return new Just(value);
}
static nothing() {
return new Nothing();
}
static of(value) {
return value == null ? new Nothing() : new Just(value);
}
}
class Just extends Maybe {
constructor(value) {
super();
this.value = value;
}
map(fn) {
return Maybe.just(fn(this.value));
}
getOrElse(_) {
return this.value;
}
isJust() { return true; }
isNothing() { return false; }
}
class Nothing extends Maybe {
map(_) {
return this;
}
getOrElse(defaultValue) {
return defaultValue;
}
isJust() { return false; }
isNothing() { return true; }
}
// 使用
const safeDivide = (a, b) =>
b === 0 ? Maybe.nothing() : Maybe.just(a / b);
const result = safeDivide(10, 2)
.map(x => x + 1)
.getOrElse(0);
// 6
Either 类型
Either 类型表示两种可能之一:成功(Right)或失败(Left)。
class Either {
static left(value) {
return new Left(value);
}
static right(value) {
return new Right(value);
}
}
class Left extends Either {
constructor(value) {
super();
this.value = value;
}
map(_) {
return this;
}
flatMap(_) {
return this;
}
fold(leftFn, _) {
return leftFn(this.value);
}
}
class Right extends Either {
constructor(value) {
super();
this.value = value;
}
map(fn) {
return Either.right(fn(this.value));
}
flatMap(fn) {
return fn(this.value);
}
fold(_, rightFn) {
return rightFn(this.value);
}
}
// 使用
const parseNumber = (str) => {
const num = Number(str);
return isNaN(num)
? Either.left(`"${str}" is not a number`)
: Either.right(num