コンテンツにスキップ

Chapter 2: 二項演算とMonoid

Chapter 1で学んだcombineLatestWithは、あらゆる2項演算をTimelineの世界に持ち込むための、汎用的なプリミティブです。この章では、そのcombineLatestWithをより具体的に、そして安全に利用するためのヘルパー関数群を定義します。

重要なのは、これから定義する2項演算が、Unit 2で学んだMonoidという数学的構造に基づいている点です。Monoidは、「ある型」「その型の2つの値を取り、同じ型の値を返す結合法則を満たす2項演算」「その演算における単位元」の3つの組で定義される代数的構造でした。

この章で示すのは、値レベルのMonoidが、combineLatestWith(Applicativeのmap2操作)によって、そのままTimelineコンテナレベルのMonoidへと、その美しい構造を保ったまま引き継がれる様子です。

これから紹介するorOf, andOf, addOf, concatOfといった関数は、すべてcombineLatestWithを特定のMonoid演算で特殊化したものです。

F#: orOF: Timeline<bool> -> Timeline<bool> -> Timeline<bool>
Section titled “F#: orOF: Timeline<bool> -> Timeline<bool> -> Timeline<bool>”
TS: orOf(timelineA: Timeline<boolean>, timelineB: Timeline<boolean>): Timeline<boolean>
Section titled “TS: orOf(timelineA: Timeline<boolean>, timelineB: Timeline<boolean>): Timeline<boolean>”

2つのTimeline<boolean>を論理和(||)で合成します。

この操作の基盤となるのは、boolean型における論理和のMonoidです。

  • : boolean

  • 2項演算: || (OR)

  • 単位元: false (false || xは常にx)

このMonoidの性質が、orOfによってTimelineレベルに引き継がれます。

// TS
// 実装は combineLatestWith を利用している
const orOf = (timelineA: Timeline<boolean>, timelineB: Timeline<boolean>): Timeline<boolean> =>
combineLatestWith((a: boolean, b: boolean) => a || b)(timelineA)(timelineB);

F#: andOf: Timeline<bool> -> Timeline<bool> -> Timeline<bool>
Section titled “F#: andOf: Timeline<bool> -> Timeline<bool> -> Timeline<bool>”
TS: andOf(timelineA: Timeline<boolean>, timelineB: Timeline<boolean>): Timeline<boolean>
Section titled “TS: andOf(timelineA: Timeline<boolean>, timelineB: Timeline<boolean>): Timeline<boolean>”

2つのTimeline<boolean>を論理積(&&)で合成します。

これは、論理積のMonoidに基づいています。

  • : boolean

  • 2項演算: && (AND)

  • 単位元: true (true && xは常にx)


F#: (F#版には直接のヘルパーなし。combineLatestWith (+) を使用)
Section titled “F#: (F#版には直接のヘルパーなし。combineLatestWith (+) を使用)”
TS: addOf(timelineA: Timeline<number>, timelineB: Timeline<number>): Timeline<number>
Section titled “TS: addOf(timelineA: Timeline<number>, timelineB: Timeline<number>): Timeline<number>”

2つのTimeline<number>を加算(+)で合成します。

これは、数値の加算におけるMonoidに基づいています。

  • : number

  • 2項演算: + (Addition)

  • 単位元: 0 (0 + xは常にx)


F#: concatOf: Timeline<'a list> -> Timeline<'a> -> Timeline<'a list>
Section titled “F#: concatOf: Timeline<'a list> -> Timeline<'a> -> Timeline<'a list>”
TS: concatOf(timelineA: Timeline<any[]>, timelineB: Timeline<any>): Timeline<any[]>
Section titled “TS: concatOf(timelineA: Timeline<any[]>, timelineB: Timeline<any>): Timeline<any[]>”

concatは、2つの配列を結合して1つの新しい配列を作る操作です。この単純な操作もまた、極めて重要なMonoidを形成します。

  • : array<'a> (任意の型の配列)

  • 2項演算: concat (配列の結合)

  • 単位元: [] (空の配列) ([]と任意の配列xを結合しても、結果はxのまま)

この「配列のMonoid」は、次章で登場するlistOfを構築するための理論的な礎となります。

ここまで見てきたように、combineLatestWithは、値レベルで定義されたMonoid(加算、論理和、配列結合など)を、その性質を一切損なうことなく、Timelineコンテナの世界へと持ち上げる役割を果たしています。

0を足しても値が変わらないように、Timeline(0)addOfで加えてもTimelineの振る舞いは変わりません。[]と結合しても配列が変わらないように、Timeline([])concatOfで結合してもTimelineの振る舞いは変わりません。

Applicative Functorのmap2操作(ここではcombineLatestWith)が、下層にあるMonoidの美しい代数的構造を、そのままコンテナレベルのMonoidへと継承させるのです。この堅牢な数学的基盤があるからこそ、我々は次章で、これらの部品を安心して組み上げ、より複雑な合成関数を構築することができます。