chengaofeng
发布于 2024-10-12 / 8 阅读
0
0

Getting started with fp-ts: Ord

原文: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

例如,这是类型numberOrd实例:

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

实例必须满足以下法则:

  1. 自反性:对于所有A中的xcompare(x, x) === 0

  2. 反对称性:如果compare(x, y) <= 0compare(y, x) <= 0,则x等于y,对于所有A中的xy

  3. 传递性:如果compare(x, y) <= 0compare(y, z) <= 0,则compare(x, z) <= 0,对于所有A中的xyz

此外,compare必须符合Eqequals

对于所有A中的xy,如果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组合器来避免一些样板代码:给定一个AOrd实例和一个从BA的函数,我们可以推导出BOrd实例:

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


评论