Menu


FAIC モナド、パート10

原文

前回のFAIC ではシンプルな「ある値に別のデータを関連付ける」モナドの例をいくつか提示した。これらも便利なんだけれど、モナド・パターンの真の力は、データに対するワークフローを表現するオブジェクトを生成する時に発揮される。C#でのいちばんいい例はシーケンスモナド IEnumerable<T> だ。実のところ、LINQの最重要ポイントはとあるモナド的ワークフローを簡単に生成できることで、そのワークフローとは「クエリー」のことだ。

何度か言ったことがある話だけど、もしLINQ入門者にたった1つだけLINQの説明をできるとするなら、こう言うだろう: クエリーオブジェクトはクエリー を表現しているんであって、クエリーの実行結果 じゃない、と。この件に関する最も基本的な例を見てみよう。「select many」クエリーだ。1 LINQ to Objectのオーバーロードされた SelectMany メソッドのうちの1つの実装がこれだ:2

すぐ気付いたと思うが、このシグネチャーはここ5、6回ほどの連載の中に登場している: SelectMany とは、シーケンスモナドの Bind 、またの名をApplySpecialFunction だ。そして、 Bind って何をするんだったっけ? このメソッドはあるワークフロー(つまりあるシーケンス)とある関数を受け取り、ワークフローの出力に対して関数を論理的に「バインド」する新しいワークフローを生成する。実例を見ながら考えてみよう:

そして、整数のシーケンスを生成するワークフローがあったとしよう:

結果はどうなった?「※ただし偶数に限る」という操作をワークフローの最後にくっつけることで、古いクエリーから新しいクエリーを生成した。この例を一般化することもできる:

でもこれではちょっと使いにくすぎる; まるっとヘルパーメソッドで抽象化した方がいい:

何を作ったかお分かりかな?まさに今、SelectMany といくつかのヘルパーメソッドを呼び出すだけで Where メソッドを実装したんだ。3 とはいえこうやって Where を実装するのはアホほど遠回りではある; C#の機能を利用すれば、もっと短くもっと効率的に実装できる。ただし言いたかったのは、Where論理的には 単なるSelectMany の呼び出しに毛が生えただけのもので、やっていることは同じだということだ: すでにあるワークフローの末尾に新しい処理を結合するだけ。もちろんこれはSelect でも同じことが言えて、全体を SelectMany で書き換えることができる:

繰り返すが、分別ある人間は実際には Select メソッドをこんな風に実装したりはしない。C#ではもっと短くてもっと効率的な実装ができるからだ。4 でも論理的にはSelectSelectMany の特別な呼び出し方の1つに過ぎない; もしもC#にもっと効率のよい機構がなかったとするなら、この実装を使ってもいい。 シーケンスを受け取って新しいシーケンスを返すクエリ内包であれば、どんなものでも全部同じことが言える; SelectMany があるからこそ、モナド的ワークフローの末尾に任意の処理を結合し、新しいワークフローを返すことができるのだ。

次回のFAIC では、モナドに対するバインド操作と「クエリー内包」の文法との関連性を見ていく。

  1. selectクエリーの方が基本的なクエリーなんじゃないかと思うかもしれないが、後でわかるように、selectはselect manyの特殊例の1つでしかない。前にもApplyFunctionApplySpecialFunction の特殊例の1つでしかないと言った。この関係性は偶然の一致ではない!Select とは、シーケンスモナドの ApplyFunction なのだ。
  2. 解説をよりわかりやすくするため、エラー処理を除いている。
  3. そしてもちろん、CreateSimpleSequence もこっそり使っている; さてどこでしょう?
  4. そして WhereSelectSelectMany があれば、Join も実装できる; とても効率の悪い Join にはなるだろうけど、作ることは可能だ。これは練習問題として残しておく。

インデックスへ戻る


Last Update: 2013-03-28 20:30:48