aleksandar-kyng-WWRL3ZGECX8-unsplash.jpg

fp-tsとEq

 
0
このエントリーをはてなブックマークに追加
Kazuki Moriyama
Kazuki Moriyama (森山 和樹)

値の等値性を司る型クラスEqの紹介。

https://gcanti.github.io/fp-ts/modules/Eq.ts.html

定義

export interface Eq<A> {
  readonly equals: (x: A, y: A) => boolean
}

equalsという等値判断関数がいるだけ。

law

Eqが満たすべきlawは3つある。

  1. 反射律: E.equals(a, a) === true
  2. 対称律: E.equals(a, b) === E.equals(b, a)
  3. 推移律: E.equals(a, b) === trueかつE.equals(b, c) === trueならばE.equals(a, c) === true

要するに要素は自分自身と等値で、順番を入れ替えて比較しても結果は同じで、ある要素と同じな別の要素同士を比較しても結果は同じである。
Eqのインスタンスを作成するときはこれらのlawを満たすようにしなければならない。

使い方

stringとかにはすでにインスタンスが用意されている。

import * as S from "fp-ts/string";
import * as Eq from "fp-ts/Eq";

console.log(S.Eq.equals("a", "a")); // => true
console.log(S.Eq.equals("a", "b")); // => false

コンビネータ

struct

オブジェクトに対するEqを別のEqから作成できる。

type Person = {
  name: string;
  age: number;
};

const personEq = Eq.struct<Person>({
  name: S.Eq,
  age: N.Eq,
});

console.log(
  personEq.equals({ name: "taro", age: 20 }, { name: "taro", age: 20 })
); // => true
console.log(
  personEq.equals({ name: "taro", age: 20 }, { name: "taro", age: 10 })
); // => false

tuple

タプルに対するEqを別のEqから作成できる。

const tuple3Eq = Eq.tuple(S.Eq, N.Eq, personEq);

console.log(
  tuple3Eq.equals(
    ["a", 1, { name: "taro", age: 20 }],
    ["a", 1, { name: "taro", age: 20 }]
  )
); // => true
console.log(
  tuple3Eq.equals(
    ["a", 1, { name: "taro", age: 20 }],
    ["a", 1, { name: "taro", age: 10 }]
  )
); // => false

もちろんタプルとして型が違うものを入れるとコンパイルエラーになる。

contramap

型Aに対するEqインスタンスとB => Aと型を変換する関数をとってEq<B>を作成する。

// numberをstringに変換したあとS.Eqで比較するEq<number>
const numEq = Eq.contramap((n: number) => n.toString())(S.Eq);

console.log(numEq.equals(1, 1)); // => true
console.log(numEq.equals(1, 2)); // => false

constructors

fromEquals

比較関数からEqを作成できる。
Eqインスタンスを作成するヘルパー関数。

const personAgeEq = Eq.fromEquals<Person>((a, b) => a.age === b.age);
// ageさえ同じなら同じPerson

console.log(
  personAgeEq.equals({ name: "taro", age: 20 }, { name: "taro", age: 20 })
); // => true
console.log(
  personAgeEq.equals({ name: "john", age: 20 }, { name: "taro", age: 20 })
); // => true

インスタンス

Contravariant

Eqに対してのContravariant。

console.log(
  Eq.Contravariant.contramap(S.Eq, (a: number) => a.toString()).equals(1, 1)
); // => true

eqStrict

Eq<unkown>、つまりどの型に対しても使用できるEqインスタンス。
比較ロジックは===で決定される。

console.log(Eq.eqStrict.equals("1", 1)); // => false

getMonoid

Eq<A>のMonoidインスタンスを作成できる。
emptyは常にtrueを返すEq、concatは2つのEqを受け取って論理積を取るようなEqを返す。

console.log(
  Eq.getMonoid<string>() // 最初の文字が同じで長さがが同じならば同じ文字列とみなす
    .concat(
      Eq.fromEquals<string>((a, b) => a[0] === b[0]),
      Eq.fromEquals<string>((a, b) => a.length === b.length)
    )
    .equals("ac", "ab")
); // => true

// 常にtrueを返す
console.log(Eq.getMonoid<string>().empty.equals("a", "b")); => true

getSemigroup

getMonoidのemptyがなくなった版。

info-outline

お知らせ

K.DEVは株式会社KDOTにより運営されています。記事の内容や会社でのITに関わる一般的なご相談に専門の社員がお答えしております。ぜひお気軽にご連絡ください。