モナドが全然わからないので,まずは写経することから始めよう。
『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);
例によってコンピューテーション式もマクロです。