在弃用 getFoldableComposition、选项、数组等之后进行重构
Refactoring after deprecation of getFoldableComposition, option, array et al
去年我花了一些时间尝试学习 fp-ts。我终于开始在项目中使用它,并且由于最近的重构,我的很多示例代码都被破坏了。我已经修复了一些破损,但正在努力解决其他问题。毫无疑问,它突出了我 FP 知识中的一个巨大整体!
我有这个:
import { strict as assert } from 'assert';
import { array } from 'fp-ts/Array';
import { getFoldableComposition, } from 'fp-ts/Foldable';
import { Monoid as MonoidString } from 'fp-ts/string'
import { none,some, option } from 'fp-ts/Option';
const F = getFoldableComposition(array, option)
assert.strictEqual(F.reduce([some('a'), none, some('c')], '', MonoidString.concat), 'ac')
getFoldableComposition、选项和数组现已弃用。 getFoldableComposition 上的评论说要改用 reduce、foldMap 或 reduceRight,所以除其他事项外,我尝试了这个。
import { strict as assert } from 'assert';
import { reduceRight } from 'fp-ts/Foldable';
import { Monoid as MonoidString } from 'fp-ts/string'
import { some } from 'fp-ts/Option';
assert.strictEqual(reduceRight([some('a'), none, some('c')], '', MonoidString.concat), 'ac')
那还没有编译,所以显然我离题太远了。
谁能告诉我替换 getFoldableComposition
的正确方法,并在我们这样做的同时,解释 'Use small, specific instances instead' 以及 option
和 [= 的含义14=]?另外,我还有什么明显做错了吗?
谢谢!
让我们从您的问题开始
what is meant by 'Use small, specific instances instead' as well for option and array?
在 fp-ts
v2.10.0 之前,类型 class 实例组合在一起作为实现多个 class 接口的单个记录,并且类型 class record 以定义了 classes 的数据类型命名。因此,对于 Array
模块,导出的 array
包含所有实例;它有 map
用于 Functor
和 ap
用于 Apply
等。对于 Option
,option
记录与所有实例一起导出。等等。
许多函数,例如 getFoldableComposition
和 sequenceT
是使用“更高类型的类型”非常通用地定义的,并且需要您为您的数据类型传入类型 class 实例想要使用的功能。因此,例如,sequenceT
要求您传递一个 Apply
实例,例如
assert.deepEqual(
sequenceT(O.option)([O.some(1), O.none]),
O.none
)
要求像这样传递这些 classes 类型的大记录实例最终导致 fp-ts
在应用程序和库代码中不能很好地进行 tree-shake,因为 JS 打包器不能静态地告诉 class 类型的哪些成员记录在哪里被访问,哪些不被访问,所以即使只使用一个,它最终也会包括所有这些成员。这会增加包的大小,最终会使用户的应用加载速度变慢 and/or 会增加使用您的库的库的包大小。
这个问题的解决方案是将大类型 class 记录分开,并为每种类型 class 提供自己的记录。所以现在每个数据类型模块都导出小型的、单独的类型 class 实例,最终大型实例记录将被删除。所以现在你会使用 sequenceT
like
assert.deepEqual(
sequenceT(O.Apply)([O.some(1), O.none]),
O.none
)
现在捆绑器知道只有 Apply
个方法被使用,并且它可以从捆绑中删除未使用的实例。
所以所有这一切的结果就是不再使用大型实例记录,而只使用较小的实例记录。
现在输入代码。
我要说的第一件事是与编译器交谈。你的代码应该给你一个编译错误。我看到的是:
所以你传递了reduceRight
太多的参数,所以让我们看看签名:
export declare function reduceRight<F extends URIS, G extends URIS>(
F: Foldable1<F>,
G: Foldable1<G>
): <B, A>(b: B, f: (a: A, b: B) => B) => (fga: Kind<F, Kind<G, A>>) => B
首先你应该注意,这个函数是 curried 并且需要三个调用才能完全评估(即它被柯里化为三个单独的函数调用)。首先它采用类型 class 实例,然后是累加器和归约函数,最后它采用我们要归约的数据类型。
所以首先它需要一个 Foldable
实例作为一种类型 Type -> Type
,另一个 Foldable
实例用于另一种(或相同)类型的类型 Type -> Type
.这就是小型与大型实例记录发挥作用的地方。您将通过 SomeDataType.Foldable
而不是 SomeDataType.someDataType
.
然后它采用类型 Type
的多态类型 B
作为 reduce(又名“累加器”)的初始值和采用多态类型 A
的二元函数种类 Type
和 B
和 returns B
。这是 reduceRight 的典型签名。
然后它需要一个看起来很吓人的类型,它正在使用更高级的类型。我会将其发音为“F of G of A”或 F<G<A>>
。最后是returnsB
,减少后的值。
听起来很复杂,但希望在这之后它看起来不会那么糟糕。
从您的代码来看,您似乎想要将 Array<Option<string>>
缩减为 string
。 Array<Option<string>>
是您要指定的更高种类的类型。您只需将“F of G of A”替换为“Array of Option of string”。所以在 reduceRight
的签名中,F
是 Array
的 Foldable
实例,而 G
是 Option
的 Foldable
实例.
如果我们传递这些实例,我们将返回一个专门用于选项数组的 reduceRight
函数。
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B =
reduceRight(A.Foldable, O.Foldable)
然后我们用初始累加器和一个减少函数来调用这个 reduce,该函数采用 Array<Option<?>>
中的值 string
和累加器的类型,这也是 string
.在您的初始代码中,您将 concat
用于 string
。这将在这里工作,您将在 string
模块的 Monoid<string>
实例中找到它。
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
import * as string from 'fp-ts/string'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B
= reduceRight(A.Foldable, O.Foldable)
const reduceRightArrayOptionStringToString: (fga: Array<O.Option<string>>) => string
= reduceRightArrayOption("", string.Monoid.concat)
终于可以带我们的Array<O.Option<string>>
了。
import * as assert from 'assert'
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
import * as string from 'fp-ts/string'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B
= reduceRight(A.Foldable, O.Foldable)
const reduceRightArrayOptionStringToString: (fga: Array<O.Option<string>>) => string
= reduceRightArrayOption("", string.Monoid.concat)
const result = reduceRightArrayOptionStringToString([
O.some('a'),
O.none,
O.some('c'),
])
assert.strictEqual(result, "ac")
为了简化所有这些,我们可以使用更惯用的 pipe
方法来调用 reduceRight
:
import * as assert from "assert"
import { reduceRight } from "fp-ts/Foldable"
import * as string from "fp-ts/string"
import * as O from "fp-ts/Option"
import * as A from "fp-ts/Array"
import { pipe } from "fp-ts/lib/function"
assert.strictEqual(
pipe(
[O.some("a"), O.none, O.some("c")],
reduceRight(A.Foldable, O.Foldable)(string.empty, string.Monoid.concat)
),
"ac"
)
我知道很多,但希望它能让您清楚地了解正在发生的事情。 reduceRight
是非常通用的,几乎没有其他 TypeScript 库尝试这样做,所以如果您花一些时间来理解它是完全正常的。更高种类的类型不是 TypeScript 的内置功能,并且 fp-ts
的方式确实有点绕过 TS 的局限性。但是继续玩耍和试验。最终一切都会开始。
去年我花了一些时间尝试学习 fp-ts。我终于开始在项目中使用它,并且由于最近的重构,我的很多示例代码都被破坏了。我已经修复了一些破损,但正在努力解决其他问题。毫无疑问,它突出了我 FP 知识中的一个巨大整体!
我有这个:
import { strict as assert } from 'assert';
import { array } from 'fp-ts/Array';
import { getFoldableComposition, } from 'fp-ts/Foldable';
import { Monoid as MonoidString } from 'fp-ts/string'
import { none,some, option } from 'fp-ts/Option';
const F = getFoldableComposition(array, option)
assert.strictEqual(F.reduce([some('a'), none, some('c')], '', MonoidString.concat), 'ac')
getFoldableComposition、选项和数组现已弃用。 getFoldableComposition 上的评论说要改用 reduce、foldMap 或 reduceRight,所以除其他事项外,我尝试了这个。
import { strict as assert } from 'assert';
import { reduceRight } from 'fp-ts/Foldable';
import { Monoid as MonoidString } from 'fp-ts/string'
import { some } from 'fp-ts/Option';
assert.strictEqual(reduceRight([some('a'), none, some('c')], '', MonoidString.concat), 'ac')
那还没有编译,所以显然我离题太远了。
谁能告诉我替换 getFoldableComposition
的正确方法,并在我们这样做的同时,解释 'Use small, specific instances instead' 以及 option
和 [= 的含义14=]?另外,我还有什么明显做错了吗?
谢谢!
让我们从您的问题开始
what is meant by 'Use small, specific instances instead' as well for option and array?
在 fp-ts
v2.10.0 之前,类型 class 实例组合在一起作为实现多个 class 接口的单个记录,并且类型 class record 以定义了 classes 的数据类型命名。因此,对于 Array
模块,导出的 array
包含所有实例;它有 map
用于 Functor
和 ap
用于 Apply
等。对于 Option
,option
记录与所有实例一起导出。等等。
许多函数,例如 getFoldableComposition
和 sequenceT
是使用“更高类型的类型”非常通用地定义的,并且需要您为您的数据类型传入类型 class 实例想要使用的功能。因此,例如,sequenceT
要求您传递一个 Apply
实例,例如
assert.deepEqual(
sequenceT(O.option)([O.some(1), O.none]),
O.none
)
要求像这样传递这些 classes 类型的大记录实例最终导致 fp-ts
在应用程序和库代码中不能很好地进行 tree-shake,因为 JS 打包器不能静态地告诉 class 类型的哪些成员记录在哪里被访问,哪些不被访问,所以即使只使用一个,它最终也会包括所有这些成员。这会增加包的大小,最终会使用户的应用加载速度变慢 and/or 会增加使用您的库的库的包大小。
这个问题的解决方案是将大类型 class 记录分开,并为每种类型 class 提供自己的记录。所以现在每个数据类型模块都导出小型的、单独的类型 class 实例,最终大型实例记录将被删除。所以现在你会使用 sequenceT
like
assert.deepEqual(
sequenceT(O.Apply)([O.some(1), O.none]),
O.none
)
现在捆绑器知道只有 Apply
个方法被使用,并且它可以从捆绑中删除未使用的实例。
所以所有这一切的结果就是不再使用大型实例记录,而只使用较小的实例记录。
现在输入代码。
我要说的第一件事是与编译器交谈。你的代码应该给你一个编译错误。我看到的是:
所以你传递了reduceRight
太多的参数,所以让我们看看签名:
export declare function reduceRight<F extends URIS, G extends URIS>(
F: Foldable1<F>,
G: Foldable1<G>
): <B, A>(b: B, f: (a: A, b: B) => B) => (fga: Kind<F, Kind<G, A>>) => B
首先你应该注意,这个函数是 curried 并且需要三个调用才能完全评估(即它被柯里化为三个单独的函数调用)。首先它采用类型 class 实例,然后是累加器和归约函数,最后它采用我们要归约的数据类型。
所以首先它需要一个 Foldable
实例作为一种类型 Type -> Type
,另一个 Foldable
实例用于另一种(或相同)类型的类型 Type -> Type
.这就是小型与大型实例记录发挥作用的地方。您将通过 SomeDataType.Foldable
而不是 SomeDataType.someDataType
.
然后它采用类型 Type
的多态类型 B
作为 reduce(又名“累加器”)的初始值和采用多态类型 A
的二元函数种类 Type
和 B
和 returns B
。这是 reduceRight 的典型签名。
然后它需要一个看起来很吓人的类型,它正在使用更高级的类型。我会将其发音为“F of G of A”或 F<G<A>>
。最后是returnsB
,减少后的值。
听起来很复杂,但希望在这之后它看起来不会那么糟糕。
从您的代码来看,您似乎想要将 Array<Option<string>>
缩减为 string
。 Array<Option<string>>
是您要指定的更高种类的类型。您只需将“F of G of A”替换为“Array of Option of string”。所以在 reduceRight
的签名中,F
是 Array
的 Foldable
实例,而 G
是 Option
的 Foldable
实例.
如果我们传递这些实例,我们将返回一个专门用于选项数组的 reduceRight
函数。
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B =
reduceRight(A.Foldable, O.Foldable)
然后我们用初始累加器和一个减少函数来调用这个 reduce,该函数采用 Array<Option<?>>
中的值 string
和累加器的类型,这也是 string
.在您的初始代码中,您将 concat
用于 string
。这将在这里工作,您将在 string
模块的 Monoid<string>
实例中找到它。
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
import * as string from 'fp-ts/string'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B
= reduceRight(A.Foldable, O.Foldable)
const reduceRightArrayOptionStringToString: (fga: Array<O.Option<string>>) => string
= reduceRightArrayOption("", string.Monoid.concat)
终于可以带我们的Array<O.Option<string>>
了。
import * as assert from 'assert'
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
import * as string from 'fp-ts/string'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B
= reduceRight(A.Foldable, O.Foldable)
const reduceRightArrayOptionStringToString: (fga: Array<O.Option<string>>) => string
= reduceRightArrayOption("", string.Monoid.concat)
const result = reduceRightArrayOptionStringToString([
O.some('a'),
O.none,
O.some('c'),
])
assert.strictEqual(result, "ac")
为了简化所有这些,我们可以使用更惯用的 pipe
方法来调用 reduceRight
:
import * as assert from "assert"
import { reduceRight } from "fp-ts/Foldable"
import * as string from "fp-ts/string"
import * as O from "fp-ts/Option"
import * as A from "fp-ts/Array"
import { pipe } from "fp-ts/lib/function"
assert.strictEqual(
pipe(
[O.some("a"), O.none, O.some("c")],
reduceRight(A.Foldable, O.Foldable)(string.empty, string.Monoid.concat)
),
"ac"
)
我知道很多,但希望它能让您清楚地了解正在发生的事情。 reduceRight
是非常通用的,几乎没有其他 TypeScript 库尝试这样做,所以如果您花一些时间来理解它是完全正常的。更高种类的类型不是 TypeScript 的内置功能,并且 fp-ts
的方式确实有点绕过 TS 的局限性。但是继续玩耍和试验。最终一切都会开始。