モナドが全然わからないので,まずは写経することから始めよう。
『FizzBuzz問題から学ぶモナド』で FizzBuzz モナドが F# で示されています。 F# と Nemerle は相性が良いので写経にはもってこいです。
まずモナド本体を書きます。
using Nemerle;
public variant FizzBuzzMonad
{
| FB {
value : int;
s : string;
}
[Alias(@>=>(m, f))]
public static Bind(m : FizzBuzzMonad, f : int -> FizzBuzzMonad) : FizzBuzzMonad
{
match (m)
{
| FB(value = x, s = y) => f(x) |> fun (_) { | FB(value = z, s = w) => FB(z, y + w) };
}
}
public static Return(x : int) : FizzBuzzMonad
{
FB(x, "");
}
public override ToString() : string
{
match (this)
{
| FB(value = x, s = "") => x.ToString();
| FB(s = y) => y;
}
}
}
>>= は既にシフト演算子に割り当てられているので,代わりに >=> という演算子を定義しています。 Alias マクロが活躍していますね。
元の記事ではコンソール出力用の関数を定義していますが,ここでは ToString メソッドのオーバーライドにしています。ちゃんと写経できていないです。関数脳になりきれていません。
コンピューテーション式を使うためのビルダーの作成。
public class FizzBuzzBuilder
{
public Bind(m : FizzBuzzMonad, f : int -> FizzBuzzMonad) : FizzBuzzMonad
{
FizzBuzzMonad.Bind(m, f);
}
public Return(x : int) : FizzBuzzMonad
{
FizzBuzzMonad.Return(x);
}
}
そして実行。
using Nemerle.Collections;
using Nemerle.ComputationExpressions;
def fb = FizzBuzzBuilder();
def fizzbuzz(n, d, s)
{
| _ when n % d == 0 => FizzBuzzMonad.FB(n, s);
| _ => comp fb { return n };
}
def fizz = fizzbuzz(_, 3, "Fizz");
def buzz = fizzbuzz(_, 5, "Buzz");
$[1..100].Map(x => fizz(x) >=> buzz).Iter(System.Console.WriteLine);
例によってコンピューテーション式もマクロです。