开始使用 fp-ts:Eq
在这个博客系列中,我会经常谈论“类型类”和“实例”,让我们看看它们是什么以及它们是如何编码的fp-ts
。
程序员通过指定一组函数或常量名称及其各自的类型来定义类型类,这些类型对于属于该类的每种类型都必须存在。
在fp-ts
类型类中,被编码为 TypeScript interface
。
类型类Eq
旨在包含允许相等的类型,其声明方式如下
interface Eq<A> {
/** returns `true` if `x` is equal to `y` */
readonly equals: (x: A, y: A) => boolean
}
该声明可以解读为
如果在某个类型上定义了一个以相应类型命名的函数,则该类型
A
属于类型类Eq
equal
那么实例呢?
程序员可以通过使用实例声明来使任何类型成为
A
给定类型类的成员,该实例声明定义了特定类型的C
所有成员的实现。C
A
在fp-ts
实例中被编码为静态字典。
例如,这是Eq
该类型的实例number
const eqNumber: Eq<number> = {
equals: (x, y) => x === y
}
实例必须满足以下规律:
- 反身性:
equals(x, x) === true
,对于x
所有A
- 对称性:
equals(x, y) === equals(y, x)
对于所有x
,y
在A
- 及物性:如果
equals(x, y) === true
且equals(y, z) === true
,则equals(x, z) === true
对于所有x
,,在y
z
A
然后,程序员可以elem
按以下方式定义一个函数(确定元素是否在数组中)
function elem<A>(E: Eq<A>): (a: A, as: Array<A>) => boolean {
return (a, as) => as.some(item => E.equals(item, a))
}
elem(eqNumber)(1, [1, 2, 3]) // true
elem(eqNumber)(4, [1, 2, 3]) // false
让我们Eq
为更复杂的类型编写一些实例
type Point = {
x: number
y: number
}
const eqPoint: Eq<Point> = {
equals: (p1, p2) => p1.x === p2.x && p1.y === p2.y
}
我们甚至可以尝试equals
通过首先检查引用相等性来优化
const eqPoint: Eq<Point> = {
equals: (p1, p2) => p1 === p2 || (p1.x === p2.x && p1.y === p2.y)
}
不过,这基本上只是样板代码。好消息是,我们可以Eq
为结构体构建一个实例,就像为每个字段Point
提供一个实例一样。Eq
事实上,该fp-ts/Eq
模块导出了一个getStructEq
组合器:
import { getStructEq } from 'fp-ts/Eq'
const eqPoint: Eq<Point> = getStructEq({
x: eqNumber,
y: eqNumber
})
我们可以继续使用getStructEq
刚刚定义的实例
type Vector = {
from: Point
to: Point
}
const eqVector: Eq<Vector> = getStructEq({
from: eqPoint,
to: eqPoint
})
getStructEq
不是提供的唯一组合器fp-ts
,这里有一个允许Eq
为数组派生实例的组合器
import { getEq } from 'fp-ts/Array'
const eqArrayOfPoints: Eq<Array<Point>> = getEq(eqPoint)
最后,构建Eq
实例的另一种有用方法是组合器:给定forcontramap
的实例和从到 的函数,我们可以派生出for的实例Eq
A
B
A
Eq
B
import { contramap } from 'fp-ts/Eq'
type User = {
userId: number
name: string
}
/** two users are equal if their `userId` field is equal */
const eqUser = contramap((user: User) => user.userId)(eqNumber)
eqUser.equals({ userId: 1, name: 'Giulio' }, { userId: 1, name: 'Giulio Canti' }) // true
eqUser.equals({ userId: 1, name: 'Giulio' }, { userId: 2, name: 'Giulio' }) // false
下一篇订单
文章来源:https://dev.to/gcanti/getting-started-with-fp-ts-setoid-39f3