2009年9月29日火曜日

モナドを C# で 01

C# で Maybe モナドっぽいものと State モナドっぽいもの。
型構成子とか関数の拡張が自動化できればよかったけれど、とりあえずごりごり記述。
確かに使う側での副作用はなさそう。

// Haskell の >>= がなんて呼ばれているかわからない…orz。とりあえず束縛。


using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

class Program
{
static void Main(string[] args)
{
var result1 =
new Maybe<string[]>(new string[] { "012", "345", null, "678" })
.Bind(ss => ss.Skip(1))
.Bind(ss => ss.Skip(1).ElementAt(0))
.Bind(s => Regex.Match(s, "[0-3]+"))
.Bind(m => m.Value);

Console.WriteLine("result1 = " + result1.Apply());
// result1 =

var result2 =
new Maybe<string[]>(new string[] { "012", "345", "3", "678" })
.Bind(ss => ss.Skip(1))
.Bind(ss => ss.Skip(1).ElementAt(0))
.Bind(s => Regex.Match(s, "[0-3]+"))
.Bind(m => m.Value);

Console.WriteLine("result2 = " + result2.Apply());
// result2 = 3

var result3 =
new State<string, string>("123456789")
.Bind<string, int>(t => new Tuple<string, int>(t.State, t.State.Length))
.Bind<int, string>(t => new Tuple<int, string>(t.State, t.State.ToString()))
.Bind<string, string>(t => new Tuple<string, string>(t.State, t.State + "010101"));

Console.WriteLine("result3 = " + result3.Apply());
// result3 = 9010101


Console.ReadLine();
}
}


// Maybe モナド?
public class Maybe<T>
where T : class
{
private Func<T> func;

public Maybe(T value)
{
this.func = new Func<T>(() => value);
}

public Maybe(Func<T> func)
{
if (func == null) throw new ArgumentNullException("func");
this.func = func;
}

public Maybe<S> Bind<S>(Func<T, S> func)
where S : class
{
if (func == null) throw new ArgumentNullException("func");
return new Maybe<S>(new Func<S>(() => { var x = this.func(); return x == null ? null : func(x); }));
}

public T Apply()
{
return this.func();
}
}



// State モナド?
public class State<V1, S1>
{
private Func<V1, Tuple<V1, S1>> func;
public State(S1 s)
{
this.func = new Func<V1, Tuple<V1, S1>>(value => new Tuple<V1, S1>(value, s));
}

public State(Func<V1, Tuple<V1, S1>> func)
{
if (func == null) throw new ArgumentNullException("func");
this.func = func;
}

public State<V2, S2> Bind<V2, S2>(Func<Tuple<V1, S1>, Tuple<V2, S2>> func)
{
if (func == null) throw new ArgumentNullException("func");
return new State<V2, S2>(
new Func<V2, Tuple<V2, S2>>(value => { var t = func(this.func(default(V1))); return new Tuple<V2, S2>(t.Value, t.State); }));
}

public S1 Apply()
{
return this.func(default(V1)).State;
}
}


// C# にも Tuple 欲しいかも。
public struct Tuple<V1, S1>
{
private V1 value;
private S1 state;

public Tuple(V1 value, S1 state)
{
this.value = value;
this.state = state;
}

public V1 Value { get { return this.value; } set { this.value = value; } }
public S1 State { get { return this.state; } set { this.state = value; } }
}

0 件のコメント:

コメントを投稿