Chapter 3: foldTimelines — Monoidによる畳み込み
Chapter 2では、Monoidを形成する二項演算を用いて2つのTimeline
を結合する方法を学びました。この章では、その概念をさらに拡張し、Timeline
のリスト(任意の数のTimeline
)を1つのTimeline
に畳み込む方法を探求します。
そのための汎用的なツールがfoldTimelines
です。これは、関数型プログラミングにおける標準的な畳み込み操作であるfold
(またはreduce
)をTimeline
の世界に応用したものであり、Chapter 2で確立したMonoidの構造に完全に依存しています。
foldTimelines
API
Section titled “foldTimelines API”F#: foldTimelines: (Timeline<'b> -> Timeline<'a> -> Timeline<'b>) -> Timeline<'b> -> list<Timeline<'a>> -> Timeline<'b>
Section titled “F#: foldTimelines: (Timeline<'b> -> Timeline<'a> -> Timeline<'b>) -> Timeline<'b> -> list<Timeline<'a>> -> Timeline<'b>”TS: foldTimelines<A, B>(timelines: readonly Timeline<A>[], initialTimeline: Timeline<B>, accumulator: (acc: Timeline<B>, current: Timeline<A>) => Timeline<B>): Timeline<B>
Section titled “TS: foldTimelines<A, B>(timelines: readonly Timeline<A>[], initialTimeline: Timeline<B>, accumulator: (acc: Timeline<B>, current: Timeline<A>) => Timeline<B>): Timeline<B>”// TS 実装export const foldTimelines = <A, B>( timelines: readonly Timeline<A>[], initialTimeline: Timeline<B>, accumulator: (acc: Timeline<B>, current: Timeline<A>) => Timeline<B>): Timeline<B> => timelines.reduce(accumulator, initialTimeline);
foldTimelines
はTimeline
のリストを受け取り、Monoidの「単位元」を初期値として、リストの要素にMonoidの「二項演算」を次々と適用することで、単一のTimeline
を生成します。
引数の内訳は、Monoidの構成要素と完全に対応しています。
timelines
: 畳み込まれるTimeline
のリスト。initialTimeline
: Monoidの単位元(畳み込みの初期値)。accumulator
: Monoidの二項演算(例:Chapter2で定義したorOf
)。
高レベルAPIの導出
Section titled “高レベルAPIの導出”foldTimelines
の真価は、anyOf
やsumOf
のような直感的な高レベルAPIが、特別な実装を必要とせず、様々なMonoidとfoldTimelines
を組み合わせるだけで実現できる点にあります。それらはfoldTimelines
の薄いラッパーに過ぎません。
F#: anyOf: list<Timeline<bool>> -> Timeline<bool>
Section titled “F#: anyOf: list<Timeline<bool>> -> Timeline<bool>”TS: anyOf(booleanTimelines: readonly Timeline<boolean>[]): Timeline<boolean>
Section titled “TS: anyOf(booleanTimelines: readonly Timeline<boolean>[]): Timeline<boolean>”// TS 実装export const anyOf = (booleanTimelines: readonly Timeline<boolean>[]): Timeline<boolean> => { return foldTimelines(booleanTimelines, FalseTimeline, orOf);};
複数のブール値タイムラインにまたがる論理和(OR)です。これは論理和Monoidを用いてfoldTimelines
を適用することで導出されます。
- 単位元:
Timeline(false)
- 二項演算:
orOf
// TS 利用例const flags = [Timeline(true), Timeline(false), Timeline(false)];const hasAnyTrue = anyOf(flags);console.log(hasAnyTrue.at(Now)); // true
F#: allOf: list<Timeline<bool>> -> Timeline<bool>
Section titled “F#: allOf: list<Timeline<bool>> -> Timeline<bool>”TS: allOf(booleanTimelines: readonly Timeline<boolean>[]): Timeline<boolean>
Section titled “TS: allOf(booleanTimelines: readonly Timeline<boolean>[]): Timeline<boolean>”// TS 実装export const allOf = (booleanTimelines: readonly Timeline<boolean>[]): Timeline<boolean> => { return foldTimelines(booleanTimelines, TrueTimeline, andOf);};
複数のブール値タイムラインにまたがる論理積(AND)です。これは論理積Monoidを用いて導出されます。
- 単位元:
Timeline(true)
- 二項演算:
andOf
// TS 利用例const flags = [Timeline(true), Timeline(true), Timeline(false)];const allTrue = allOf(flags);console.log(allTrue.at(Now)); // false
TS: sumOf(numberTimelines: readonly Timeline<number>[]): Timeline<number>
Section titled “TS: sumOf(numberTimelines: readonly Timeline<number>[]): Timeline<number>”// TS 実装export const sumOf = (numberTimelines: readonly Timeline<number>[]): Timeline<number> => { return foldTimelines(numberTimelines, Timeline(0), addOf);};
複数の数値タイムラインの合計です。これは加算Monoidを用いて導出されます。
- 単位元:
Timeline(0)
- 二項演算:
addOf
// TS 利用例const numbers = [Timeline(10), Timeline(20), Timeline(30)];const total = sumOf(numbers);console.log(total.at(Now)); // 60
TS: maxOf(timelines): Timeline<number>
Section titled “TS: maxOf(timelines): Timeline<number>”// TS 実装export const maxOf = (numberTimelines: readonly Timeline<number>[]): Timeline<number> => { return foldTimelines(numberTimelines, Timeline(-Infinity), maxOf2);};
複数の数値タイムラインにまたがる最大値です。これは最大値Monoidから導出されます。
- 単位元:
Timeline(-Infinity)
- 二項演算: タイムライン用の二項
max
関数。
// TS 利用例const numbers = [Timeline(10), Timeline(50), Timeline(30)];const maximum = maxOf(numbers);console.log(maximum.at(Now)); // 50
TS: minOf(timelines): Timeline<number>
Section titled “TS: minOf(timelines): Timeline<number>”// TS 実装export const minOf = (numberTimelines: readonly Timeline<number>[]): Timeline<number> => { return foldTimelines(numberTimelines, Timeline(Infinity), minOf2);};
複数の数値タイムラインにまたがる最小値です。これは最小値Monoidから導出されます。
- 単位元:
Timeline(Infinity)
- 二項演算: タイムライン用の二項
min
関数。
// TS 利用例const numbers = [Timeline(10), Timeline(50), Timeline(30)];const minimum = minOf(numbers);console.log(minimum.at(Now)); // 10
averageOf
Section titled “averageOf”TS: averageOf(timelines): Timeline<number>
Section titled “TS: averageOf(timelines): Timeline<number>”// TS 実装export const averageOf = (numberTimelines: readonly Timeline<number>[]): Timeline<number> => { if (numberTimelines.length === 0) return Timeline(0); return sumOf(numberTimelines).map(sum => sum / numberTimelines.length);};
複数の数値タイムラインの平均です。これは単純なMonoidではありませんが、概念的には似ています。sumOf
とタイムラインの数を探すために畳み込み、それらを割る新しいタイムラインを作成することで導出できます。
// TS 利用例const numbers = [Timeline(10), Timeline(20), Timeline(30)];const avg = averageOf(numbers);console.log(avg.at(Now)); // 20
listOf
Section titled “listOf”F#: listOf: list<Timeline<'a>> -> Timeline<'a list>
Section titled “F#: listOf: list<Timeline<'a>> -> Timeline<'a list>”TS: listOf<A>(timelines: readonly Timeline<A>[]): Timeline<A[]>
Section titled “TS: listOf<A>(timelines: readonly Timeline<A>[]): Timeline<A[]>”// TS 実装export const listOf = <A>( timelines: readonly Timeline<A>[]): Timeline<A[]> => { const emptyArrayTimeline = Timeline<A[]>([]); return foldTimelines(timelines, emptyArrayTimeline, concatOf) as Timeline<A[]>;};
複数のタイムラインを、それらの値の配列を保持する単一のタイムラインに結合します。これはChapter2で強調された配列Monoidから導出されます。
- 単位元:
Timeline([])
- 二項演算:
concatOf
この関数は複数のTimeline
を、それらの値の配列を持つ単一のTimeline
に単純に結合できるため、AIがコードを生成する際に扱いやすく、優先的に使用される傾向があります。
// TS 利用例const items = [Timeline("a"), Timeline("b"), Timeline("c")];const list = listOf(items);console.log(list.at(Now)); // ["a", "b", "c"]
anyOf
やsumOf
を含む高レベルAPI群が、場当たり的で個別な関数の集まりではないことが明らかになりました。それらはすべて、単一の汎用的な抽象概念であるfoldTimelines
と、各型が持つMonoid構造から、数学的な必然性をもって導出されています。
combineLatestWith
(Applicative) → 二項演算(Monoid) → foldTimelines
→ 高レベルAPIというこの階層構造こそが、このライブラリの設計美と、コードの重複を排除した堅牢性の核となっています。