如果您想留在“内部fp-ts
”,您可以执行以下操作:
import { flow, pipe, tuple } from "fp-ts/function"
import * as Mn from "fp-ts/Monoid"
import * as N from "fp-ts/number"
import * as O from "fp-ts/Option"
import * as Ord from "fp-ts/Ord"
import * as RM from "fp-ts/ReadonlyMap"
import * as RT from "fp-ts/ReadonlyTuple"
import * as Sg from "fp-ts/Semigroup"
import * as S from "fp-ts/string"
type Person = string
type Age = number
type PersonAge = readonly [Person, Age]
const ordByAge: Ord.Ord<PersonAge> = pipe(N.Ord, Ord.contramap(RT.snd))
const monoidMaxByAge: Mn.Monoid<O.Option<PersonAge>> = pipe(
ordByAge,
Sg.max,
O.getMonoid
)
const oldestPerson = (ps: ReadonlyMap<Person, Age>) =>
pipe(
ps,
RM.foldMapWithIndex(S.Ord)(monoidMaxByAge)(flow(tuple, O.some)),
O.map(RT.fst)
)
这非常冗长,并没有解决您对性能的担忧。
我觉得烦人的一件事是它需要一个 ORD。也许这是使函数更具确定性所必需的,但排序对于许多折叠/归约任务并不重要,并且会降低性能。
你说得对,它是关于决定论的。本机 JSMap
以插入顺序存储条目,因此如果没有Ord
键的实例,此oldestPerson
函数可能会为两个给定Map
的 s 产生不同的结果,除了插入顺序之外它们是等效的。我会认为这是出乎意料的行为。
这是我期望持有的:
import * as assert from "assert"
const aliceBob: ReadonlyMap<Person, Age> = new Map([
["Alice", 25],
["Bob", 25],
])
const bobAlice: ReadonlyMap<Person, Age> = new Map([
["Bob", 25],
["Alice", 25],
])
const emptyMap: ReadonlyMap<Person, Age> = new Map([])
assert.deepStrictEqual(oldestPerson(aliceBob), oldestPerson(bobAlice))
assert.deepStrictEqual(oldestPerson(emptyMap), O.none)
我同意其中的许多函数fp-ts/ReadonlyMap
效率不高,我认为它们只是作为本机数据结构的最小“功能 API”包装器,不能很好地处理不变性和确定性。如果性能更受关注,我可能会像您一样使用本机方法。
一个叫的东西,getFoldableWithIndex
但我不知道如何使用它或让它与 Typescript 一起编译。
getFoldableWithIndex
将返回一个FoldableWithIndex
类型类实例,在这里对您没有用处。这是一个值,您可以将其作为参数传递给需要FoldableWithIndex
实例的函数,我什至不确定是否有一个很好的示例 one 。有一个traverse_
in fp-ts/Foldable
which 接受一个Foldable
实例,但没有对应traverseWithIndex_
的 ,尽管该假设函数将接受一个FoldableWithIndex
实例。中有一些 函数,fp-ts/FoldableWithIndex
但这些函数是关于将两个实例组合FoldableWithIndex
成另一个实例(这在这里没有用)。
您可以FoldableWithIndex
直接使用它:类型类实例是一个带有uncurried foldMapWithIndex
方法的对象,因此如果您想让原始代码片段更加详细,您可以这样做:
const oldestPerson = (ps: ReadonlyMap<Person, Age>) =>
pipe(
RM.getFoldableWithIndex(S.Ord).foldMapWithIndex(monoidMaxByAge)(
ps,
flow(tuple, O.some)
),
O.map(RT.fst)
)
但这些实例并不是真的要直接在 a 中使用pipe
,这就是我使用顶级RM.foldMapWithIndex
. 这里有更多关于类型类如何fp-ts
工作的信息。