让我们从你的问题开始
对于选项和数组,“改用小型特定实例”是什么意思?
在fp-ts
v2.10.0 之前,类型类实例被组合在一起作为实现多个类接口的单个记录,并且类型类记录以定义类的数据类型命名。因此,对于Array
模块,array
导出包含所有实例;它有map
forFunctor
和ap
forApply
等。对于Option
,option
记录与所有实例一起导出。等等。
许多函数,例如getFoldableComposition
和sequenceT
使用“高级类型”非常通用地定义,并要求您传入类型类实例以获取您希望函数使用的数据类型。因此,例如,sequenceT
要求您传递一个Apply
实例,例如
assert.deepEqual(
sequenceT(O.option)([O.some(1), O.none]),
O.none
)
要求像这样传递这些类型类实例的大记录最终导致fp-ts
应用程序和库代码中的 tree-shake 不好,因为 JS 捆绑器无法静态地判断类型类记录的哪些成员在哪里被访问,哪些不是t,所以即使只使用了一个,它最终也会包括所有这些。这会增加包大小,最终使用户的应用加载速度变慢和/或增加使用库的库的包大小。
解决这个问题的方法是将大类型类记录分开,并为每个类型类提供自己的记录。所以现在每个数据类型模块都导出小的、单独的类型类实例,最终巨型实例记录将被删除。所以现在你会使用sequenceT
like
assert.deepEqual(
sequenceT(O.Apply)([O.some(1), O.none]),
O.none
)
现在捆绑器知道只有Apply
方法被使用,它可以从捆绑中删除未使用的实例。
所以这一切的结果就是不再使用大型实例记录,而只使用较小的实例记录。
现在为您的代码。
我要说的第一件事是与编译器交谈。您的代码应该给您一个编译错误。我看到的是这样的:
![reduceRight 期望 2 个参数,但得到 3 个。](https://i.stack.imgur.com/iemzs.png)
所以你传递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
首先您应该注意,此功能需要三个应用程序才能完全评估。首先它采用类型类实例,然后是累加器和归约函数,最后是我们正在归约的数据类型。
所以首先它需要一个Foldable
kind 类型的实例Type -> Type
,以及Foldable
另一个(或相同)类型 kind 的实例Type -> Type
。这就是小型与大型实例记录发挥作用的地方。你会通过SomeDataType.Foldable
而不是SomeDataType.someDataType
.
然后它将多态类型B
的类型Type
作为 reduce(也称为“累加器”)的初始值,以及一个二进制函数,该函数采用多态A
类型Type
并B
返回B
。这是 reduceRight 的典型签名。
然后它需要一个看起来很吓人的类型,它正在使用更高种类的类型。我会将其发音为“F of G of A”或F<G<A>>
. 最后它返回B
,减少的值。
听起来很复杂,但希望在此之后它不会看起来那么糟糕。
通过查看您的代码,您似乎想将 a 缩减Array<Option<string>>
为 a string
。Array<Option<string>>
是您要指定的更高种类的类型。您只需将“F of G of A”替换为“字符串选项数组”。因此,在 的签名中reduceRight
,F
是 的Foldable
实例,Array
并且G
是 的Foldable
实例Option
。
如果我们传递这些实例,我们将返回一个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<?>>
isstring
和累加器的类型,也就是string
。在您的初始代码中,您使用concat
的是 for string
。这将在这里工作,你会在模块中的Monoid<string>
实例上找到它。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 限制的方式无疑是一种 hack。但请继续玩耍和尝试。这一切最终都会开始点击。