Monadic Comprehensions and Functional Composition with Query Expressions

Preview:

DESCRIPTION

Build monads using the C# language with a C# style, then use the appropriate methods to ensure the LINQ query syntax works with this functional design pattern. After describing monads, we will cut the middleman and apply the same techniques directly to objects and functions to achieve better results with a declarative syntax.

Citation preview

Monadic Comprehensions and Functional Composition with

Query Expressions

Chris Eargle

LINQ to Objects

Oh, that’s a monad!

List Monad

LINQ to Objectsfrom x in new[] {1, 2, 3}from y in new[] {1, 2, 3}select Tuple.Create(x, y)

List Monaddo x <- [1,2,3] y <- [1,2,3] return (x,y)

Query Expressions

var words = new[] { "the", "quick", "brown", "fox"};

IEnumerable<string> query = from word in words select word;

var query = from word in words orderby word.Length select word;

var query = from word in words orderby word.Length select word.Replace(‘o’, ‘a’);

var query = words.OrderBy(w => w.Length) .Select(w => w.Replace('o', 'a'));

Monad

Functional Design Pattern

ReturnBind

public struct Monad<T> { private T value;

public Monad(T value) { this.value = value; }

public Monad<U> Bind<U>(Func<T, Monad<U>> func) { return func(value); } }

public static class Monad { public static Monad<T> Create<T>(T value) { return new Monad<T>(value); } }

var monad = Monad.Create(1);var monad2 = monad.Bind(v => Monad.Create(v));

Assert.IsInstanceOfType(monad, typeof(Monad<int>));Assert.AreEqual(monad, monad2);Assert.AreNotSame(monad, monad2);

Identity Monad

public struct Identity<T> { T value; public T Value { get { return value; } } public Identity(T value) { this.value = value; } }

public static class Identity { public static Identity<T> Create<T>(T value) { return new Identity<T>(value); } }

var result = from x in Identity.Create(1) select x.ToString();

Assert.AreEqual("1", result.Value);

public Identity<TResult> Select<TResult>(Func<T, TResult> selector)

{ return Identity.Create(selector(value)); }

var result = from x in Identity.Create(2) from y in Identity.Create(3) select x + y;

Assert.AreEqual(5, result.Value);

public Identity<TResult> SelectMany<U, TResult>( Func<T, Identity<U>> selector, Func<T, U, TResult> resultSelector){ return Identity.Create(resultSelector(value,

selector(value).Value));}

var result = from x in Identity.Create(2) from y in Identity.Create(3) select x + y;

Assert.AreEqual(5, result.Value);

Cut the Middle Man

var result = from x in 2 from y in 3 select x + y;

Assert.AreEqual(5, result);

public static TResult SelectMany<T1, T2, TResult>( this T1 source, Func<T1, T2> selector, Func<T1, T2, TResult> resultSelector){ return resultSelector(source, selector(source));}

Tuples

var result = from info in ConferenceInfo() from dates in ConferenceDates() select new { Name = info.Item1, StartDate = dates.Item1, EndDate = dates.Item3 };

Assert.AreEqual("IEEE ICCSIT", result.Name);

Assert.AreEqual(july9th, result.StartDate);

Assert.AreEqual(july11th, result.EndDate);

Continuation Monad

public class Continuation<R, A> { private readonly Func<Func<A, R>, R> value; internal Continuation(Func<Func<A, R>, R> func) { this.value = func; } public R Run(Func<A, R> k) { return value(k); }}

public static class Continuation { public static Continuation<R, A> Create<R, A>(Func<A> func) { return Continuation.Create<R, A>(k => k(func())); } public static Continuation<R, A> Create<R, A>(Func<Func<A, R>, R> func) { return new Continuation<R, A>(func); } }

public static Continuation<R, int> Square<R>(int x){ return Continuation.Create<R, int>(() => x * x);}

public static Continuation<R, double> Hypotenuse<R>(int x, int y)

{ return from h in Square<R>(x) from w in Square<R>(y) select Math.Sqrt(h + w);}

Assert.AreEqual(5, Hypotenuse<double>(3, 4).Run(n => n));

Cut the Middle Man

Func<int, int> square = x => x * x;Func<int, double> squareRoot = x => Math.Sqrt(x);

var hypotenuse = from h in square from w in square select squareRoot(h + w);

Assert.AreEqual(5, hypotenuse(3, 4));

public static Func<T1, T2, TResult3>SelectMany<T1, T2, TResult, TResult2, TResult3>( this Func<T1, TResult> leftFunc, Func<T1, Func<T2, TResult2>> rightFunc, Func<TResult, TResult2, TResult3> selector) { return (x, y) => selector(leftFunc(x), rightFunc(x)(y)); }

Func<int, int> f = x => x + 2;

var result = from x in f select x * 2;

Assert.AreEqual(6, result(1));

public static Func<T, TResult2> Select <T, TResult, TResult2>( this Func<T, TResult> func, Func<TResult, TResult2> selector) { return a => selector(func(a)); }

public static Func<Int32, Double> Sum(this Func<Int32, Double> func, int start = 0) { return end => { Double result = default(Double); for (int k = start; k <= end; k++) { result += func(k); } return result; }; }

Factorial

Func<int, int> f = x => x;

var factorial = from x in f.Sum() where x != 0 select x ?? 1;

Assert.AreEqual(1, factorial(0)); Assert.AreEqual(6, factorial(3));

public static Func<T, dynamic> Where<T, TResult>( this Func<T, TResult> func, Func<TResult, bool> predicate){ return a => { TResult result = func(a); return predicate(result) ? result as dynamic : null; };}

Func<Int32, Double> f = k => 4 * Math.Pow(-1, k) / (2.0 * k + 1); var calculatePi = f.Sum(); Assert.AreEqual(3.14159, calculatePi(200000), 7);

monadic.codeplex.com

Recommended