原文:https://dev.to/gcanti/getting-started-with-fp-ts-ord-5f1e
这篇文章的标题是《Getting started with fp-ts: Ord》,由Giulio Canti在2019年3月13日发表,并在2021年10月27日更新。文章是关于如何在fp-ts
库中使用Ord
类型类的入门指南。
在之前的关于Eq
的博客文章中,我们讨论了等价的概念。在这篇文章中,我们将讨论顺序的概念。
一个旨在包含承认全序的类型的类型类Ord
,声明如下:
import { Eq } from 'fp-ts/Eq'
type Ordering = -1 | 0 | 1
interface Ord<A> extends Eq<A> {
readonly compare: (x: A, y: A) => Ordering
}
我们说:
如果
compare(x, y)
等于-1
,则x < y
如果
compare(x, y)
等于0
,则x
等于y
如果
compare(x, y)
等于1
,则x > y
因此,我们可以这样说,如果compare(x, y) <= 0
,则x <= y
。
例如,这是类型number
的Ord
实例:
const ordNumber: Ord<number> = {
equals: (x, y) => x === y,
compare: (x, y) => (x < y ? -1 : x > y ? 1 : 0)
}
实例必须满足以下法则:
自反性:对于所有
A
中的x
,compare(x, x) === 0
反对称性:如果
compare(x, y) <= 0
且compare(y, x) <= 0
,则x
等于y
,对于所有A
中的x
,y
传递性:如果
compare(x, y) <= 0
且compare(y, z) <= 0
,则compare(x, z) <= 0
,对于所有A
中的x
,y
,z
此外,compare
必须符合Eq
的equals
:
对于所有A
中的x
,y
,如果compare(x, y) === 0
,则equals(x, y) === true
注意,可以从compare
推导出一个合法的equals
:
equals: (x, y) => compare(x, y) === 0
实际上,fp-ts/Ord
模块导出了一个方便的fromCompare
助手,它允许你通过简单地指定一个compare
函数来定义一个Ord
实例:
import { Ord, fromCompare } from 'fp-ts/Ord'
const ordNumber: Ord<number> = fromCompare((x, y) => (x < y ? -1 : x > y ? 1 : 0))
程序员可以按照以下方式定义一个函数min
(它取两个值中的最小值):
function min<A>(O: Ord<A>): (x: A, y: A) => A {
return (x, y) => (O.compare(x, y) === 1 ? y : x)
}
min(ordNumber)(2, 1) // 1
全序可能在我们谈论数字时看起来是显而易见的(即要么x <= y
要么y <= x
),但情况并不总是这样。让我们考虑一个更复杂的类型:
type User = {
name: string
age: number
}
我们如何定义一个Ord<User>
?
这真的取决于你,但一个可能的选择是按他们的age
对用户进行排序:
const byAge: Ord<User> = fromCompare((x, y) => ordNumber.compare(x.age, y.age))
我们可以通过使用contramap
组合器来避免一些样板代码:给定一个A
的Ord
实例和一个从B
到A
的函数,我们可以推导出B
的Ord
实例:
import { contramap } from 'fp-ts/Ord'
const byAge: Ord<User> = contramap((user: User) => user.age)(ordNumber)
现在我们可以使用min
来挑选两个用户中较年轻的一个:
const getYounger = min(byAge)
getYounger({ name: 'Guido', age: 48 }, { name: 'Giulio', age: 45 }) // { name: 'Giulio', age: 45 }
如果我们想要选择年长的呢?我们需要“反转顺序”,或者更准确地说,获取对偶顺序。
幸运的是,还有一个导出的组合器用于这个目的:
import { getDualOrd } from 'fp-ts/Ord'
function max<A>(O: Ord<A>): (x: A, y: A) => A {
return min(getDualOrd(O))
}
const getOlder = max(byAge)
getOlder({ name: 'Guido', age: 48 }, { name: 'Giulio', age: 45 }) // { name: 'Guido', age: 48 }
下一篇文章将讨论Semigroup
。