FizzBuzz モナド in Nemerle


モナドが全然わからないので,まずは写経することから始めよう。

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);

例によってコンピューテーション式もマクロです。