Suppose we want a set of unique { x, y }
points to plot on a graph.
this expression:
new Set([
{ x: 1, y: 1 },
{ x: 1, y: 1 },
]);
evaluates to: Set { { x: 1, y: 1}, { x : 1, y: 1} }
which is likely not what we want.
Let's see the signature of the same constructor in the Set module contained in the fp-ts library:
declare const fromArray: <A>(E: Eq<A>) => (as: Array<A>) => Set<A>;
where the Eq
typeclass is defined as:
interface Eq<A> {
readonly equals: (x: A, y: A) => boolean;
}
Thus, fromArray
takes first an Eq
instance where it is up to the consumer to specify the meaning of the equivalence in his particular domain.
interface Point {
x: number;
y: number;
}
const eqPoint: Eq<Point> = {
equals: (p1, p2) => p1 === p2 || (p1.x === p2.x && p1.y === p2.y),
};
We have defined what does it means for two points to be equal. They are either the same point (by reference), or they have the same coordinates
fromArray(eqPoint)([
{ x: 1, y: 1 },
{ x: 1, y: 1 },
]);
Now correctly evaluates to Set { { x: 1, y: 1} }
Takeaway: the concept of equivalence is vague and it is often a good idea to encode it's meaning in our domain business.
Absolutely, didn't write it like that to make it obvious for people that aren't accustomed to the Eq typeclass what was happening.