光学结构
Lens 怎样聚焦必定存在的一个值?
- Lens: 将 getter 与不可变 setter 配成一个结构;
view: 读取焦点;set: 替换焦点;over: 用函数变换焦点;- 可组合: 多个 Lens 可聚焦深层嵌套字段;
const Lens = (get, set) => ({ get, set });
const nameLens = Lens(
(person) => person.name,
(name, person) => ({ ...person, name }),
);
Prism 怎样聚焦可能不存在的分支?
- Prism: 聚焦和类型中的某个分支,匹配可能失败;
preview: 尝试取出焦点,返回Option或空值;review: 从焦点重建完整数据;
const Prism = (preview, review) => ({ preview, review });
const integerPrism = Prism((text) => (/^-?\d+$/.test(text) ? Number(text) : null), String);
integerPrism.preview("42"); // 42
integerPrism.preview("hello"); // null
integerPrism.review(42); // '42'
Iso 怎样表达无损双向转换?
- Iso: 同一信息两种表示之间的可逆映射;
- 组成:
to :: S -> A与from :: A -> S; - 往返律:
from(to(x)) = x,to(from(y)) = y;
const Iso = (to, from) => ({ to, from });
const temperature = Iso(
(celsius) => (celsius * 9) / 5 + 32,
(fahrenheit) => ((fahrenheit - 32) * 5) / 9,
);
temperature.to(100); // 212
temperature.from(212); // 100
Traversal 怎样同时聚焦多个值?
- Traversal: 一次聚焦零个、一个或多个值;
getAll: 收集全部焦点;modify: 不可变地变换每个焦点;
const evenTraversal = {
getAll: (xs) => xs.filter((n) => n % 2 === 0),
modify: (fn, xs) => xs.map((n) => (n % 2 === 0 ? fn(n) : n)),
};
evenTraversal.getAll([1, 2, 3, 4, 5, 6]); // [2, 4, 6]
evenTraversal.modify((n) => n * 10, [1, 2, 3, 4, 5, 6]);
// [1, 20, 3, 40, 5, 60]
四种光学结构的焦点数量怎样比较?
| 结构 | 焦点数量 | 适用形状 |
|---|---|---|
| Lens | 恰好 1 | 必有字段、积类型 |
| Prism | 0 或 1 | 可选分支、和类型 |
| Iso | 恰好 1 | 可逆的整体表示 |
| Traversal | 0 到多个 | 集合、树、过滤子集 |