移植 齐奥光学 到类型脚本
flowchart TD
Iso --> Lens
Iso --> Prism
Lens --> ReversedPrism
ReversedPrism --> Optional
Prism --> Optional
Optional --> Getter
Optional --> Setter
Getter --> Optic
Setter --> Optic
特征
- 光子的统一代表。所有光学的构成方式相同,因为它们都是同一数据类型的实例(
Optic(中文(简体) ) - 一体化。用于
effect数据结构,例如Option财务报告和审定财务报表Either。 。 。 .
导言
@fp-ts/optic 是一个库,可以方便地根据光学的单一表现来修改较大数据结构的部件,作为取出器和定出器的组合.
@fp-ts/optic 具有光学的统一代表性,深度 effect 集成, 帮助错误消息 .
信贷和担保
这个图书馆受到以下项目的启发:
非常感谢我的赞助商,他们开发了 @fp-ts/optic 也许吧.
如果你还想 成为提案人 确保这个图书馆继续改进和接受维护 GitHub 赞助者简介
所需资源
- 类型脚本 5.4 或更新
- 这个
strict启用您的旗帜tsconfig.json文件
{
// ...
"compilerOptions": {
// ...
"strict": true
}
}
开始
安装 阿尔法 版本 :
npm install @fp-ts/optic
3⁄4 ̄ ̧漯B。本套件的发布主要是为了接收早期反馈,对于贡献者来说,在这个开发阶段,我们不能保证APIs的稳定性,认为每个版本都包含断续续的更改.
一旦安装了库,您可以从库中导入必要的类型和功能 @fp-ts/optic 模块.
import * as Optic from "@fp-ts/optic";
内容提要
假设我们有个员工 并且我们得给他公司街名上的第一个角色.
import * as Option from "effect/Option";
interface Street {
readonly num: number;
readonly name: Option.Option<string>;
}
interface Address {
readonly city: string;
readonly street: Street;
}
interface Company {
readonly name: string;
readonly address: Address;
}
interface Employee {
readonly name: string;
readonly company: Company;
}
const from: Employee = {
name: "john",
company: {
name: "awesome inc",
address: {
city: "london",
street: {
num: 23,
name: Option.some("high street"),
},
},
},
};
const to: Employee = {
name: "john",
company: {
name: "awesome inc",
address: {
city: "london",
street: {
num: 23,
name: Option.some("High street"),
},
},
},
};
让我们看看我们能做什么 @fp-ts/optic
import * as Optic from "@fp-ts/optic";
import * as StringOptic from "@fp-ts/optic/String";
import * as String from "effect/String";
import * as assert from "node:assert";
const _firstChar: Optic.Optional<Employee, string> = Optic.id<Employee>()
.at("company")
.at("address")
.at("street")
.at("name")
.some()
.compose(StringOptic.index(0));
const capitalizeName = Optic.modify(_firstChar)(String.toUpperCase);
assert.deepStrictEqual(capitalizeName(from), to);
理解视觉
@fp-ts/optic 其基础是光学的单一表示,作为取出器和定出器的组合.
export interface Optic<
in GetWhole,
in SetWholeBefore,
in SetPiece,
out GetError,
out SetError,
out GetPiece,
out SetWholeAfter,
> {
readonly getOptic: (
GetWhole: GetWhole
) => Either<readonly [GetError, SetWholeAfter], GetPiece>;
readonly setOptic: (
SetPiece: SetPiece
) => (
SetWholeBefore: SetWholeBefore
) => Either<readonly [SetError, SetWholeAfter], SetWholeAfter>;
}
获取器可以使用一些更大的类型结构 GetWhole 并获得它的一部分类型 GetPiece。它可能因类型错误而失败 GetError 因为我们试图得到的部分 可能不存在 在更大的结构.
摆放器有它的能力,因为它有些类型 SetPiece 和原类型结构 SetWholeBefore,返回新的类型结构 SetWholeAfter设置会因类型错误而失败 SetError 因为我们试图设置的棋子 可能不存在的结构.
镜头
A 类 Lens 是一种访问产品类型领域的光学技术,例如tuple或struct.
这个 GetError 类型 Lens 实值 never 因为我们总能得到一个产品类型的领域。 这个 SetError 类型也是 never 因为我们总能把产品类型的领域设置到一个新的值.
在这种情况下 GetWhole, (中文(简体) ), SetWholeBefore,以及 SetWholeAfter 类型相同,并代表产品类型。 这个 GetPiece 财务报告和审定财务报表 SetPiece 类型也是相同的,并代表字段.
因此,我们:
export interface Lens<in out S, in out A>
extends Optic<S, S, A, never, never, A, S> {}
简化的签名是:
export interface Lens<in out S, in out A> {
readonly getOptic: (s: S) => Either<never, A>;
readonly setOptic: (a: A) => (s: S) => Either<never, S>;
}
这完全符合我们上面的描述。 透镜是一种光学,我们总能得到更大的结构的一部分, 并且赋予一个原始的结构,我们总能在那个结构中设定一个新的价值.
棱镜
A 类 Prism 是一种光学,可以访问一个总和型的相机,例如: Left 或者说 Right 案件数量 Either。 。 。 .
用棱镜从更大的数据结构中获取一部分可能会失败,因为我们试图访问的案例可能不存在. 例如,我们可能试图进入一个 Either 但其中之一其实是 Left。 。 。 .
我们使用数据类型 Error 来模拟不同的方式 获取或设定 光学会失败。 所以说 GetError 棱镜类型 Error。 。 。 .
这个 SetError 棱镜类型 never 因为鉴于某种产品类型的一种情况,我们总能返回一种产品类型的新价值,因为产品类型的每一种情况都是产品类型的一种情况.
棱镜也不同于镜头,因为我们不需要任何原始结构来设定. 一种产品类型只包括它的案例, 所以如果我们有我们想要设定的新的案例价值, 我们可以仅仅使用这个价值, 而不需要原始的结构.
我们用 unknown 联 合 国 SetWholeBefore 类型,表示我们不需要任何原始结构来设置新值.
因此,棱镜的定义是:
export interface Prism<in out S, in out A>
extends Optic<S, unknown, A, Error, never, A, S> {}
而简化的签名是:
export interface Prism<in out S, in out A> {
readonly getOptic: (s: S) => Either<Error, A>;
readonly setOptic: (a: A) => (s: unknown) => Either<never, S>;
}
这同样符合我们的描述。 棱镜是一种光学,我们也许无法获得一个值,但总是可以设定一个值,事实上不需要任何原始结构来设定.
其它
@fp-ts/optic 支持多种其他光学:
- 可选。一个
Optional是一种光学,它访问一个更大的结构的一部分,其中访问的部分可能不存在,而且该结构不仅包含该部分。 两者GetError财务报告和审定财务报表SetError类型是Error因为这个部分在结构中可能不存在,而设置中确实需要原始结构,因为它包含的不仅仅是这个部分. - 伊苏。一个
Iso是一个光学的,它访问一个结构的一部分, 结构中除了部分之外,什么都不是。 两者GetError财务报告和审定财务报表SetError类型是never编辑SetWholeBefore类型是unknown。 。 。 . - 盖特A级
Getter光学只允许获得一个值。 这个SetWholeBefore财务报告和审定财务报表SetPiece类型是never因为它永远无法设置. - 编辑器A级
Setter是一个只允许设置值的光学。 这个GetWhole类型是never因为它不可能永远得到.
每种光学的多态性版本也比较多,使得数据结构的种类和前后部分不同. 例如,a PolyPrism 可以让我们找到一个合适的案子 Either<A, B> 设置一个 C 要返回的数值 Either<A, C>。 。 。 .
计费表
透视构造器
| 图像 | 名称 | 鉴于 | 改为: |
|---|---|---|---|
| 伊苏 | 实值 | S => A, (中文(简体) ), A => S | Iso<S, A> |
| 编号 | Iso<S, S> | ||
| 镜头 | 镜头 | S => A, (中文(简体) ), A => S => S | Lens<S, A> |
| 时间 | Key | Lens<S, S[Key]> | |
| 选择 | Key | Lens<S, Pick<S, Key>> | |
| 删除 | Key | Lens<S, Omit<S, Key>> | |
| 棱镜 | 棱镜 | S => Either<Error, A>, (中文(简体) ), A => S | Prism<S, A> |
| 多边棱镜 | S => Either<[Error, T], A>, (中文(简体) ), B => T | PolyPrism<S, T, A, B> | |
| 骗局 | Prism<A[], [A, A[]]> | ||
| 非无 | Prism<A, NonNullable<A>> | ||
| 有点 | Prism<Option<A>, A> | ||
| 过滤器 | Predicate<S> | Prism<S, S> | |
| 过滤器 | Refinement<S, A> | Prism<S, A> | |
| 可选 | 选项 | S => Either<Error, A>, (中文(简体) ), A => S => Either<Error, S> | Optional<S, A> |
| 多选项 | S => Either<[Error, T], A>, (中文(简体) ), B => S => Either<[Error, T], T> | PolyOptional<S, T, A, B> | |
| 指数 | number | Optional<A[], A> | |
| 键 | string | Optional<{ []: A }, A> | |
| 头 | Optional<A[], A> | ||
| 尾声 | Optional<A[], A[]> | ||
| 找到第一个 | Predicate<A> | Optional<A[], A> | |
| 找到第一个 | Refinement<A, B> | Optional<A[], B> |
Getter / Stetter APIs 图像
| 名称 | 鉴于 | 改为: |
|---|---|---|
| 起来 | Lens<S, A>, (中文(简体) ), S | A |
| 解码 | Prism<S, A>, (中文(简体) ), S | Either<Error, A> |
| 编码 | Prism<S, A>, (中文(简体) ), A | S |
| 获得 Ordify 编辑 | PolyOptional<S, T, A, B>, (中文(简体) ), S | Either<T, A> |
| 修改 | Optional<S, A>, (中文(简体) ), A => A | S => S |
| 替换 | Setter<S, A>, (中文(简体) ), A, (中文(简体) ), S | S |
| 替换选项 | Setter<S, A>, (中文(简体) ), A, (中文(简体) ), S | Option<S> |
| 获取选项 | Getter<S, A>, (中文(简体) ), S | Option<A> |
基本用途
编号
这个 id 光学是一种代表身份函数的特殊光学,它简单地返回它的输入而不变. 可以认为是光学的"基案",从中可以建造出更复杂的光学.
这个 id 光学被定义为单通类型,这意味着它只有一个可能的值. 这使得它很容易成为建造更大型光学的起点,因为它不需要任何参数或配置.
import * as Optic from "@fp-ts/optic";
interface Whole {
readonly a: string;
readonly b: number;
readonly c: boolean;
}
// create an iso that focuses on the 'Whole' data structure
const _a: Optic.Iso<Whole, Whole> = Optic.id<Whole>();
编曲
这个 compose 方法是一种实用函数,可以将两个或两个以上的光学组合成一个光学.
import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
// This is the type of the data structure that the lens will be operating on.
interface Whole {
readonly a: string;
readonly b: number;
readonly c: boolean;
}
// This creates a lens that focuses on the 'a' field within the 'Whole' object.
const _a: Optic.Lens<Whole, string> =
// The 'id' function creates an identity lens for the 'Whole' type.
Optic.id<Whole>()
// The 'compose' method combines the identity lens with an 'at' lens,
// which selects the 'a' field within the 'Whole' object.
.compose(Optic.at("a"));
// Now we can use the '_a' lens to view and modify the 'a' field of a 'Whole' object.
const whole: Whole = {
a: "foo",
b: 42,
c: true,
};
// Use the 'get' function to view the 'a' field of the 'whole' object.
const result: string = pipe(whole, Optic.get(_a)); // returns "foo"
// Use the 'replace' function to update the 'a' field of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_a)("bar")); // returns { a: "bar", b: 42, c: true }
时间
这个 at 方法是一种实用函数,它创造了一个光学功能,它侧重于数据结构内的特定字段.
import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
// This is the type of the data structure that the lens will be operating on.
interface Whole {
readonly a: string;
readonly b: number;
readonly c: boolean;
}
// This creates a lens that focuses on the 'a' field within the 'Whole' object.
const _a: Optic.Lens<Whole, string> =
// The 'id' function creates an identity lens for the 'Whole' type.
Optic.id<Whole>()
// The 'at' method selects the 'a' key within the 'Whole' object,
// resulting in a lens that is focused on that field.
.at("a");
// Now we can use the '_a' lens to view and modify the 'a' field of a 'Whole' object.
const whole: Whole = {
a: "foo",
b: 42,
c: true,
};
// Use the 'get' function to view the 'a' field of the 'whole' object.
const result: string = pipe(whole, Optic.get(_a)); // returns "foo"
// Use the 'replace' function to update the 'a' field of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_a)("bar")); // returns { a: "bar", b: 42, c: true }
选择
这个 pick 方法是一种实用函数,它创建了光学,侧重于数据结构内的一组密钥.
import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
// This is the type of the data structure that the lens will be operating on.
interface Whole {
readonly a: string;
readonly b: number;
readonly c: boolean;
}
// This creates a lens that focuses on the 'a' and 'b' fields within the 'Whole' object.
const _ab: Optic.Lens<Whole, { readonly a: string; readonly b: number }> =
// The 'id' function creates an identity lens for the 'Whole' type.
Optic.id<Whole>()
// The 'pick' method selects the 'a' and 'b' keys within the 'Whole' object,
// resulting in a lens that is focused on those fields.
.pick("a", "b");
// Now we can use the '_ab' lens to view and modify the 'a' and 'b' fields of a 'Whole' object.
const whole: Whole = {
a: "foo",
b: 42,
c: true,
};
// Use the 'get' function to view the 'a' and 'b' fields of the 'whole' object.
const result: { readonly a: string; readonly b: number } = pipe(
whole,
Optic.get(_ab)
); // returns { a: "foo", b: 42 }
// Use the 'replace' function to update the 'a' and 'b' fields of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_ab)({ a: "bar", b: 23 })); // returns { a: "bar", b: 23, c: true }
删除
这个 omit 方法是一个工具函数,它创建一个将一组密钥排除在结构外的透镜。 当您想要专注于一个数据结构的子集而忽略某些字段时,这可能会有用.
import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
interface Whole {
readonly a: string;
readonly b: number;
readonly c: boolean;
}
// This creates a lens that focuses on the 'a' and 'c' fields within the 'Whole' object.
const _ac: Optic.Lens<Whole, { readonly a: string; readonly c: boolean }> =
// The 'id' function creates an identity lens for the 'Whole' type.
Optic.id<Whole>()
// The 'omit' method excludes the 'b' key within the 'Whole' object,
// resulting in a lens that is focused on the 'a' and 'c' fields.
.omit("b");
// Now we can use the '_ac' lens to view and modify the 'a' and 'c' fields of a 'Whole' object.
const whole: Whole = {
a: "foo",
b: 42,
c: true,
};
// Use the 'get' function to view the 'a' and 'c' fields of the 'whole' object.
const result: { readonly a: string; readonly c: boolean } = pipe(
whole,
Optic.get(_ac)
); // returns { a: "foo", c: true }
// Use the 'replace' function to update the 'a' and 'c' fields of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_ac)({ a: "bar", c: false })); // returns { a: "bar", b: 42, c: false }
过滤器
这个 filter 方法是一种实用函数,它创造了一个光学函数,它侧重于一个数据结构的元素,与指定的上游相匹配.
import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
import type { Option } from "effect/Option";
// This is the type of the data structure that the prism will be operating on.
interface Whole {
readonly a: number;
}
// This creates an `Optional` that focuses on the 'a' field within the 'Whole' object,
// and only includes values that are even numbers.
const _evenA: Optic.Optional<Whole, number> =
// The 'id' function creates an identity prism for the 'Whole' type.
Optic.id<Whole>()
// The 'at' method selects the 'a' key within the 'Whole' object,
// resulting in a `Lens` that is focused on that field.
.at("a")
// The 'filter' method only includes values that are even numbers.
.filter((a) => a % 2 === 0);
// Now we can use the '_evenA' `Optional` to view and modify the 'a' field of a 'Whole' object,
// but only if the value is an even number.
const whole: Whole = {
a: 2,
};
// Use the 'getOption' function to view the 'a' field of the 'whole' object.
const result: Option<number> = pipe(whole, Optic.getOption(_evenA)); // returns some(2)
// Use the 'replace' function to update the 'a' field of the 'whole' object.
const updated: Whole = pipe(whole, Optic.replace(_evenA)(4)); // returns { a: 4 }
非无
这个 nonNullable 方法是一个创建 Prism ,侧重于无效类型的不可变值。 如果您想要操纵或提取无效类型的值, 但想要忽略 null 数值.
import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
import type { Option } from "effect/Option";
const _nonNullString: Optic.Prism<string | null, string> = Optic.id<
string | null
>().nonNullable();
const result1: Option<string> = pipe("foo", Optic.getOption(_nonNullString)); // returns some("foo")
const result2: Option<string> = pipe(null, Optic.getOption(_nonNullString)); // returns none
有点
这个 some 方法是一种效用函数,可以创建一个侧重于光学的光学函数 Some 情况 Option 数据类型。 此光学允许您查看并修改其中包含的值 Some 情况 Option。 。 。 .
import { pipe } from "effect";
import * as Option from "effect/Option";
import * as Optic from "@fp-ts/optic";
// This creates a prism that focuses on the 'Some' case of the 'Option<number>' object.
const _some: Optic.Prism<Option.Option<number>, number> = Optic.id<
Option.Option<number>
>().some();
const option: Option.Option<number> = Option.some(42);
const result: Option.Option<number> = pipe(option, Optic.getOption(_some)); // returns some(42)
const updated: Option.Option<number> = pipe(option, Optic.replace(_some)(23)); // returns some(23)
指数
这个 index 方法创建 Optional 光学,其重点是a ReadonlyArray编辑 Optional 光学允许您在指定的索引中查看值,或者 None 如果索引不存在。 你也可以使用这个 Optional 光学可以更新指定索引中的值,如果存在的话.
import { pipe } from "effect";
import * as Optic from "@fp-ts/optic";
import type { Option } from "effect/Option";
const _index2: Optic.Optional<ReadonlyArray<number>, number> = Optic.id<
ReadonlyArray<number>
>().index(2);
const arr: ReadonlyArray<number> = [1, 2, 3, 4];
const result: Option<number> = pipe(arr, Optic.getOption(_index2)); // some(3)
const updated1: ReadonlyArray<number> = pipe(arr, Optic.replace(_index2)(42)); // [1, 2, 42, 4]
const updated2: ReadonlyArray<number> = pipe(arr, Optic.replace(_index2)(10)); // [1, 2, 10, 4]
const updated3: ReadonlyArray<number> = pipe([], Optic.replace(_index2)(10)); // []
键
这个 key 方法是允许您创建 Optional 以索引签名的特定密钥(有字符串索引签名的类型)为重点的光学.
import { pipe } from "effect";
import type { Option } from "effect/Option";
import * as Optic from "@fp-ts/optic";
interface Data {
readonly [key: string]: number;
}
// Create an Optional optic that focuses on the 'foo' key.
const _foo: Optic.Optional<Data, number> = Optic.id<Data>().key("foo");
const data: Data = {
foo: 1,
bar: 2,
};
// Use the 'getOption' function to view the value of the 'foo' key.
const fooValue: Option<number> = pipe(data, Optic.getOption(_foo)); // returns some(1)
// Use the 'replace' function to update the value of the 'foo' key.
const updatedData: Data = pipe(data, Optic.replace(_foo)(10)); // returns { foo: 10, bar: 2 }
文档
许可证
《麻省理工学院许可证》