FP TS Ord 偏序关系 - kscarrot/blog GitHub Wiki

在上一篇有关Eq的文章中,我们讨论了相等的概念,在这篇博客,我们继续讨论排序的概念. 一个Ord类旨在通过声明一个总是允许排序类型的类型类,被描述为下面的方式:

import { Eq } from 'fp-ts/lib/Eq'

type Ordering = -1 | 0 | 1

interface Ord<A> extends Eq<A> {
  readonly compare: (x: A, y: A) => Ordering
}
  • x < y compare 返回 -1
  • x =y compare 返回 0
  • x > y compare 返回 1

下面是一个用number类型实例化Ord类的例子:

const ordNumber: Ord<number> = {
  equals: (x, y) => x === y,
  compare: (x, y) => (x < y ? -1 : x > y ? 1 : 0)
}

实例必须满足下面三个规则: 1.自反性:compare(x, x) === 0,对于所有A中的x 2.反对称性:如果compare(x, y) <= 0compare(y, x) <= 0,那么equals (x,y) === true,对于所有A中的x,y 3.传递性:如果 compare(x, y) <= 0compare(y, z) <= 0那么compare(x, z) <= 0,对于所有A中的x,y

所以compare包含了 Eq.equals,即compare(x,y) === 0equails(x,y)===ture是等价的.一个严格的equal能被compare用下面的方式推导出来

equals: (x, y) => compare(x, y) === 0

事实上fp-ts/lib/Ord导出了一个fromCompare的辅助函数能够帮助你从简单的从偏序关系里推导出一个相等关系.

import { Ord, fromCompare } from 'fp-ts/lib/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

当我们在讨论数字的时候,这个例子看起来是非常显然的,不过并不总是这样,让我们考虑一些更加复杂的类型

type User = {
  name: string
  age: number
}

我们应该如何定义一个关于User的偏序关系? 这取决于如何去定义,一种颗星的方式是用用户的年龄作为排序的依据

const byAge: Ord<User> = fromCompare((x, y) => ordNumber.compare(x.age, y.age))

我们可以使用 contramap这个组合子去避免写太多的模板代码.contramap的作用是从一个对于A的Ord的实例,和一个将B转化为A的函数中推导出B的Ord的实例

import { contramap } from 'fp-ts/lib/Ord'

const byAge: Ord<User> = contramap((user: User) => user.age)(ordNumber)

现在我们就能从两个user中使用min挑选出更年轻的那个

const getYounger = min(byAge)

getYounger({ name: 'Guido', age: 48 }, { name: 'Giulio', age: 45 }) // { name: 'Giulio', age: 45 }

如果我们需要更年长的用户呢,理论上我们需要一个反向的偏序关系,用更理论的定义来面熟就是 dual order 幸运的是我们有另一个导出的组合子来做这件事:

import { getDualOrd } from 'fp-ts/lib/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 }
⚠️ **GitHub.com Fallback** ⚠️