跳到主要内容

光学结构

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必有字段、积类型
Prism0 或 1可选分支、和类型
Iso恰好 1可逆的整体表示
Traversal0 到多个集合、树、过滤子集