フリーモナドを用いた曲レコメンデーション
2025-08-11 16:40:25
原文: Song recommendations with free monads by Mark Seemann
黄金のハンマー
この記事は、大規模データ処理に直面したときの関数型プログラミングによる設計の代替アプローチに関するシリーズの一部です。これまでの記事では、関数型プログラミングを用いて例題を解くためのさまざまな選択肢を紹介してきました。Recawr サンドイッチパターンの適用、関数型コンビネータの利用、パイプ&フィルターの使用といった手法には、それぞれトレードオフが存在しました。アーキテクチャの実用性に制限があったり、あまり関数型とは言えなかったりしました。
今回と、次の3記事では、ついに普遍的な解決策に手を伸ばします。それがフリーモナドです。
ここで言う「普遍的」とは、「(言語がサポートしていれば)常に実装可能である」という意味です。ただし、それが常に最善の選択であるとは限りません。
当然ながら、この選択にもトレードオフがあります。
いつもそうですが(時々書き忘れますが)、これ以降の記事は選べる道を示すものであり、何かを指示するものではありません。この方法でなければならないと主張する意図はありません。あくまで一例として提示します。
最後の手段
フリーモナドが常に使える手段であるなら、なぜ最初からそれを使わないのでしょうか?なぜ記事シリーズの終盤に取り上げることになったのでしょうか?
理由はいくつもあります。
フリーモナドは「高度な」技法であり、多くのプログラマには黒魔術のように映る可能性があります。チームがフリーモナドを理解していない状態で無理に導入しても、良い結果にはつながらないでしょう。
この手の問題を示す話を紹介します。フリーモナドではなく、DI(依存性注入)コンテナの話です。あるチームで半年間、臨時のリード開発者としてコンサルティングをしていたときのことでした。複数の技術選定を行い、良い選択も悪い選択もありました。その中で悪い選択の1つが、Castle Windsor という DI コンテナの採用でした。
Castle Windsor 自体は決して悪いライブラリではありません。調査を行い、書籍も執筆した上で、最高のDIコンテナだと判断したのです。
問題は、チームの他のメンバーにとってそれが「自動的な魔法」に見えていたことです。彼らは優秀なプログラマーではありましたが、Castle Windsor の API を学ぶことに興味を持っていませんでした。自分たちの本質的な業務ではないと考え、維持コストに見合うだけのメリットを感じていなかったのです。
私が在籍している間は付き合ってくれましたが、「私が去ったらすぐに削除する」と明言されました。その間、私は Castle Windsor に関するボトルネックになっていました。その技術に関することはすべて私を通す必要があったのです。なぜなら私だけがその仕組みを理解していたからです。
数年後、別のプロジェクトで再びそのチームとコードベースに戻ったとき、彼らは約束通り Castle Windsor を削除し、代わりにPure DIを採用していました。よくぞやってくれたと思ったものです。
この経験から、DI コンテナに知的魅力はあっても、ほとんどの場合正しい選択肢ではないと痛感しました。
フリーモナドも同様のカテゴリに属すると感じています。似たような実体験はありませんが、他の手法が尽きた場合にのみ選択するべきだと慎重に考えています。判断に迷ったときは、F# のフリーモナドレシピの記事にある意思決定フローチャートを参照することをお勧めします。
言語サポート
フリーモナドには、言語によるサポートの差異という問題もあります。Haskellでは非常に相性が良いため、このサブシリーズではまず Haskell の例から始めます。
- Haskell のフリーモナドを用いた曲レコメンデーション
- F# のフリーモナドを用いた曲レコメンデーション
- C# のフリーモナドを用いた曲レコメンデーション
F# ではもう少し下準備が必要ですが、必要なインフラを追加すれば、フリーモナドを使った「ユーザーコード」は問題なく書けます。
一方、C# はフリーモナドを快適に使うための言語機能を十分に備えていません。C# でフリーモナドを使うことはお勧めしません。C# 版の記事はあくまでデモンストレーションを目的としたものです。
これらの記事はフリーモナドを初学者に紹介するものではありません。よりやさしい導入については、純粋なインタラクションシリーズをご覧ください。
まとめ
他の手段がすべてうまくいかず、それでも純粋関数を書かなければならない場合は、フリーモナドを検討してください。誰にでも、どんな言語にも向いているわけではありませんが、大規模なデータセットだったり、ユーザーや環境との複雑なインタラクションが求められる問題に対して、関数型的な解決策を提供してくれます。
次回: Haskell版 フリーモナドを用いた曲レコメンデーション