コンピュテーション式


コンピュテーション式は制御フローを簡潔に記述するためのマクロです。いわゆるモナドと呼ばれるものを扱うための構文を提供します。しかしコンピュテーション式はコンピュテーション式であって,モナドを扱うための便利な構文ではありますが,モナドを扱わないといけないということではありません。本文ではモナドについての説明を放棄し,コンピュテーション式をコンテナ型を扱うための構文と捉えて記述していきます。コンテナ型を M[_] のように書けば,コンピュテーション式は M の中で行われる手続きを記述するための構文です。

コンピュテーション式に関するクラスやマクロは Nemerle.ComputationExpressions.dll と Nemerle.ComputationExpressions.Macros.dll に定義されています。マクロ参照に Nemerle.ComputationExpressions.Macros.dll を,通常の参照に Nemerle.ComputationExpressions.dll を追加する必要があります[A]

コンピュテーション式は,ビルダークラスと呼ばれる特定のメソッドを持つクラスを定義することで,コンピュテーション式の共通の構文を,コンパイル時にビルダークラスのメソッドを利用する形に展開します。以下で見ていきます。

構文

コンピュテーション式は以下のような形式をとります。

comp ビルダークラスのインスタンス
{
   式本体
}

より具体的な例を挙げると,以下のようになります。

def nlist = NListBuilder();
def result = comp nlist
{
   defcomp x = $[1..9];
   defcomp y = $[1..9];
   return x * y;
}

例で出てきた defcompreturn といったキーワードは,ビルダークラス (ここでは NListBuilder) の対応するメソッドに展開されます。式本体で利用される構文は次の通りです。

キーワード 説明
defcomp コンテナの中身に変数を束縛します。
callcomp コンテナの中身に対する操作を行います。
return,
yield
コンテナの中身を指定し,コンテナを返します。
returncomp,
yieldcomp
コンテナの中身を指定し,その中身を返します。
if,
when,
unless,
while,
do .. while,
repeat
foreach,
for,
using,
try .. catch .. finally
コンテナ内での各操作を表します。

ビルダークラスの定義

ビルダークラスを定義するには,特定のシグネチャを持つメソッド[B]をいくつか定義するだけです。具体的には以下のメソッドを定義します。

メソッド 対応するキーワード コメント
Bind[T, U](M[T], T -> M[U]) : M[U] defcomp
Bind[T, U](M[T], void -> M[U]) : M[U] callcomp
Combine[T](M[T], M[T]) : M[T] yield, return 結合。
Delay[T](void -> M[T]) : M[T] 遅延実行。
ForEach[T, U](Seq[T], T -> M[U]) : M[U] foreach
Return[T](T) : M[T] return
ReturnComp[T](M[T]) : M[T] returncomp
Run[T, U](M[T] -> U) : U コンピュテーション式の実行。
TryCatch[T](M[T], Exception -> M[T]) : M[T] try .. catch
TryFinally[T](M[T], void -> void) : M[T] try .. finally
Using[T, U](M[T], T -> M[U]) : M[U] where U : IDisposable using
While[T](void -> bool, M[T]) : M[T] while, do .. while
Yield[T](T) : M[T] yield
YieldComp[T](M[T]) : M[T] yieldcomp
Zero[T]() : M[T] when, unless, foreach, while 処理中で void が返される場合に対応。

利用するキーワードに応じて必要なメソッドを定義すれば良いでしょう[C]

たとえば最初の例の NListBuilder を次のように実装すれば, result は九九の結果になります。

public class NListBuilder
{
   public Bind[T, U](m : list[T], f : T -> list[U]) : list[U]
   {
      m.Map(f).Concat();
   }

   public Return[T](x : T) : list[T]
   {
      [x];
   }
}

// 再掲
def nlist = NListBuilder();
def result = comp nlist
{
   defcomp x = $[1..9];
   defcomp y = $[1..9];
   return x * y;
}

以下のように展開されます。

nlist.Bind(
   $[1..9],
   x => nlist.Bind(
      $[1..9],
      y => nlist.Return(x * y)
   )
)

参考文献

脚注

  1. 使用する構文・メソッドよっては Nemerle.ComputationExpressions.dll は必要ありません。 []
  2. 実際はコンピュテーション式の構文がクラスメソッドに展開されるだけなので,指定された通りのシグネチャでなくても,引数の数と展開された時の型のつじつまが合えばコンパイルできます。 []
  3. たとえばモナドのための構文として利用したければ, BindReturn をモナド則を満たすように定義します。 []