去年我花了一些时间尝试学习 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')
Run Code Online (Sandbox Code Playgroud)
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')
Run Code Online (Sandbox Code Playgroud)
这甚至都没有编译,所以显然我偏离了基地。
有人可以向我展示正确的替换方法吗getFoldableComposition?在我们进行替换时,请解释一下 和 的“使用小型的特定实例代替”的含义是option什么array?另外,我还有什么明显做错的事情吗?
谢谢你!
让我们从你的问题开始
对于选项和数组来说,“使用小的、特定的实例代替”是什么意思?
在 v2.10.0之前fp-ts,类型类实例被分组在一起作为实现多个类的接口的单个记录,并且类型类记录以定义类的数据类型命名。因此,对于Array模块,array导出包含所有实例;它有mapforFunctor和apforApply等。对于Option,option记录与所有实例一起导出。等等。
许多函数(例如getFoldableComposition和 )sequenceT都是使用“高级类型”非常通用地定义的,并且要求您传入您希望函数使用的数据类型的类型类实例。因此,例如,sequenceT要求您传递一个Apply实例,例如
assert.deepEqual(
sequenceT(O.option)([O.some(1), O.none]),
O.none
)
Run Code Online (Sandbox Code Playgroud)
要求像这样传递类型类实例的这些大记录,最终导致fp-ts应用程序和库代码中的树摇动效果不佳,因为 JS 捆绑器无法静态地告诉类型类记录的哪些成员正在被访问,哪些成员没有被访问。 t,所以即使只使用了一个,它最终也会包括所有这些。这会增加包大小,最终使用户的应用程序加载速度变慢和/或增加使用库的库的包大小。
这个问题的解决方案是将大类型类记录分开,并为每个类型类提供自己的记录。因此,现在每个数据类型模块都会导出小型的、单独的类型类实例,最终大型实例记录将被删除。所以现在你会使用sequenceT像
assert.deepEqual(
sequenceT(O.Apply)([O.some(1), O.none]),
O.none
)
Run Code Online (Sandbox Code Playgroud)
现在,捆绑器知道只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
Run Code Online (Sandbox Code Playgroud)
首先您应该注意的是,这个函数是柯里化的,需要三次调用才能完全评估(即它被柯里化为三个单独的函数调用)。首先它采用类型类实例,然后是累加器和归约函数,最后它采用我们要归约的数据类型。
因此,首先它需要Foldable一个 kind 类型的实例Type -> Type,以及Foldable另一个(或相同)类型 kind 的另一个实例Type -> Type。这就是小型实例与大型实例记录发挥作用的地方。你会通过SomeDataType.Foldable而不是SomeDataType.someDataType。
然后,它将Bkind 的多态类型Type作为 reduce 的初始值(也称为“累加器”),并返回一个采用Akind 的多态类型Type和的二元函数。这是reduceRight 的典型签名。BB
然后它需要一个看起来可怕的类型,它利用了更高类型的类型。我会将其发音为“F of G of A”或F<G<A>>。最后它返回B,即减少后的值。
听起来很复杂,但希望在此之后它不会看起来那么糟糕。
从您的代码来看,您似乎想将 an 减少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)
Run Code Online (Sandbox Code Playgroud)
然后我们用初始累加器和一个归约函数调用这个reduce,该函数接受其中的值Array<Option<?>>和string累加器的类型,这也是string。在您的初始代码中,您使用的是concatfor 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)
Run Code Online (Sandbox Code Playgroud)
最后,它准备好接受我们的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")
Run Code Online (Sandbox Code Playgroud)
为了简化这一切,我们可以使用更惯用的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"
)
Run Code Online (Sandbox Code Playgroud)
我知道这已经很多了,但希望它能让大家清楚地了解正在发生的事情。reduceRight非常通用,几乎没有其他 TypeScript 库尝试这样做,因此如果您需要一段时间才能理解它,这是完全正常的。更高种类的类型并不是 TypeScript 的内置功能,而且fp-ts它的实现方式无疑是为了解决 TS 的限制而采取的一些技巧。但请继续玩耍和尝试。最终一切都会开始顺利。
| 归档时间: |
|
| 查看次数: |
497 次 |
| 最近记录: |