
遅延評価と並行処理を備えた、
Dart のための関数型プログラミング。
FxDart は FxTS の Dart 移植版です。 同期・非同期データに対する遅延パイプラインを合成するためのライブラリで、 1秒かかるリクエスト6回の逐次処理を2秒の並行バッチに変えるのに 必要なのはメソッド呼び出し1つだけです。
📒 実際のアプリで確認: Daily Ledger — FxDart で作られたフル機能アプリ →
⚖️ Dart vs FxDart — 実践課題 53 問を並べて比較 →
⚡ RxDart vs FxDart — push 対 pull、正直な判定 50 件 →
▲ これは実際に動きます。コードを編集して Run を押してください。 本物の Dart コンパイラでコンパイルされ、ブラウザ上で実行されます。
FxDart とは?
FxDart は FxTS のプログラミングモデルを Dart にもたらします。コレクションと非同期データを 変換するための、小さく合成可能な約120個の関数のツールキットです。 3つの考え方がその核にあります。
- 遅延評価 —
map、filter、takeといった演算子はパイプラインを組み立てるだけで、 終端演算子(toList、each、reduceなど)が値を引き出すまで何も処理しません。100万要素の range を.take(3)で処理すると、計算されるのはちょうど3件です。 - 同期と非同期で同一のモデル — 同じ演算子名が素の
Iterableでも、FxDart のプル型非同期シーケンスであるFxAsyncIterableでも動作します(Streamとの 相互変換も可能です)。 - 宣言的な並行処理 —
concurrent(n)は 上流のパイプラインに対して、順序を保ったままn件ずつ 評価するよう要求します。FxTS を代表する機能であり、 Dart に忠実に移植されています。
時間とともに届くイベント、そして値としての失敗は、残る二つの面にあります。 どの面か? が 判断で、このページは紹介のままです。
なぜ必要なのか?
Dart には既に Iterable と Stream があります。FxDart が
真価を発揮するのは、それらでは手が届かない領域です。
| 課題 | 素の Dart | FxDart |
|---|---|---|
| API 呼び出しの並行数を n に制限しつつ順序を保つ | 手作業のキュー、Completer、緻密な管理 |
.map(fetch).concurrent(3) |
| 非同期の変換パイプライン | Stream はプッシュ型。await、バックプレッシャー、遅延評価を混在させると複雑になる |
プル型のチェーン — 各値は要求されたときにだけ計算される |
| データ加工(group、index、count、partition、zip、chunk など) | その都度ループを手書き | 概念ごとに十分にテストされた名前付き関数が1つ |
| 読みやすい多段変換 | ネストした呼び出しや中間変数 | 左から右へ読める fx() チェーン、完全に型付き |
長所 & 短所
✓ 長所
- 遅延評価が標準 — パイプラインは短絡評価され、要求された値だけが計算されます。
- 順序を保つ並行処理が演算子1つで実現できます:
concurrent(n)、完了順ならconcurrentPool(n)。 - 完全に型付きのチェーン —
fx()は端から端まで型推論を維持します。同期演算子はネイティブのIterableに対する素の関数なので、通常の Dart とそのまま相互運用できます。 - 小さく目的の明確な関数群 — 変換 / フィルタ / スライス / 結合 / 集約 / オブジェクト / ユーティリティを網羅する約120個の演算子。
- 実績あるセマンティクス — FxTS から挙動を移植し、そのテスト850件以上も併せて移植しています。
- 依存関係ゼロ — 純粋な Dart のみ。
✗ 短所
- データラストのカリー化がない — Dart には関数オーバーロードがないため、FxTS のカリー化された
pipeスタイルはチェーンになります。動的なpipe()は静的型を失います。 - 非同期の抽象がもう1つ増える —
Streamでは並行処理のバックチャネルを表現できないためFxAsyncIterableが存在します。相互変換は簡単ですが、学ぶべき概念が1つ増えます。 - 学習コスト — 遅延パイプラインで考えることは、命令的なループとは異なります。
- 常に最速とは限らない — ごく小さなホットループでは、手書きの
forが演算子の合成より速いことがあります。FxDart は明快さと I/O バウンドな処理に最適化しています。 - そのまま移植できない TS API もある — それらは Dart らしい書き方に置き換えられています:
curryは型付きの.curried拡張ゲッターになり、旧来の名前は移行用に非推奨スタブとして残されています。
並行処理を体験する
300 ms かかる疑似リクエストを6回 — 逐次なら約1.8 秒、
concurrent(3) なら約0.6 秒です。数字を変えて試してみてください: