Skip to content

Instantly share code, notes, and snippets.

@ldematte
Last active December 15, 2015 07:38
Show Gist options
  • Save ldematte/5224484 to your computer and use it in GitHub Desktop.
Save ldematte/5224484 to your computer and use it in GitHub Desktop.
Testing different solutions for async (continuation) Monads in C#, including using LINQ Syntax
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Dematte.Tests
{
// Reproduce the Async/Workflow pattern from F#
// using C# classes, complete with a continuation monad.
// For learning purposes only
public class MyAsyncParams<T>
{
public MyAsyncParams(Action<T> cont)
{
this.cont = cont;
}
public Action<T> cont;
}
public class MyAsync<T>
{
Func<MyAsyncParams<T>, Unit> myAsyncFunc;
public MyAsync(Func<MyAsyncParams<T>, Unit> func)
{
this.myAsyncFunc = func;
}
public void Apply(MyAsyncParams<T> args)
{
myAsyncFunc(args);
}
}
public class AsyncUtils
{
public static MyAsync<U> Bind<T, U>(MyAsync<T> e, Func<T, MyAsync<U>> rest)
{
Func<MyAsyncParams<U>, Unit> myAsyncFunc = delegate(MyAsyncParams<U> args)
{
Action<T> cont = delegate(T a)
{
var res = rest(a);
res.Apply(args);
};
var otherArgs = new MyAsyncParams<T>(cont);
e.Apply(otherArgs);
// Can be omitted transforming Func<MyAsyncParams<U>, Unit> into
// -> Action<MyAsyncParams<U>>
return Unit.One;
};
return new MyAsync<U>(myAsyncFunc);
}
}
// Only here to see the steps of contruction
public abstract class MyIntermediateBaseAsync<T>
{
//Changed the myAsyncFunc from a delegate to an overridable function
public abstract void myAsyncFunc(Action<T> args);
public void Apply(Action<T> args)
{
myAsyncFunc(args);
}
}
// My base asynch in C#
public abstract class MyBaseAsync<T>
{
//Fused myAsyncFunc and Apply
public abstract void Apply(Action<T> args);
}
public class MyBindAsync<T, U> : MyBaseAsync<U>
{
private MyBaseAsync<T> e;
private Func<T, MyBaseAsync<U>> rest;
public MyBindAsync(MyBaseAsync<T> e, Func<T, MyBaseAsync<U>> rest)
{
this.e = e;
this.rest = rest;
}
public override void Apply(Action<U> args)
{
Action<T> cont = delegate(T a)
{
var res = rest(a);
res.Apply(args);
};
e.Apply(cont);
}
}
public class MyIarAsync<T> : MyBaseAsync<T>
{
private Func<System.AsyncCallback, object, System.IAsyncResult> beginFunc;
private Func<System.IAsyncResult, T> endFunc;
public MyIarAsync(Func<System.AsyncCallback, object, System.IAsyncResult> beginFunc,
Func<System.IAsyncResult, T> endFunc)
{
this.beginFunc = beginFunc;
this.endFunc = endFunc;
}
public override void Apply(Action<T> args)
{
var callback = new System.AsyncCallback(delegate(IAsyncResult iar)
{
var retval = endFunc(iar);
args(retval);
});
beginFunc(callback, null);
}
}
// let's change names
// What is that takes a function and, when some conditions are met,
// runs it?
public interface IContinuable<T>
{
void ContinueWith(Action<T> args);
}
// Now try to reverse it again: from classes to function.
// This should allow us 1) (Maybe!) more efficiency: every Bind is a heap allocation
// (but then calling a virtual method is pretty quick)
// 2) modify method names, change parameter types slightly -> have linq syntax
public static class Continuable
{
// The OO versione of building a continuation that is the "binding" (composition?) of two
// continuations
public static IContinuable<U> Bind<T, U>(IContinuable<T> e, Func<T, IContinuable<U>> rest)
{
return new BindContinuables<T, U>(e, rest);
}
// And its functional version, obtained by reversing our original process.
// IContinuable<T> -> Action<Action<T>> (or Func<Func<T, Unit>, Unit>, or Func<Func<T, Answer>, Answer>?)
// or delegate Answer Continuation<T,Answer>(Func<T,Answer> k);
public static Action<Action<U>> Bind<T, U>(Action<Action<T>> e, Func<T, Action<Action<U>>> f)
{
Action<Action<U>> ret = delegate(Action<U> args)
{
Action<T> cont = delegate(T a)
{
var res = f(a);
res(args);
};
e(cont);
};
return ret;
}
// Wow! Same result as http://blogs.msdn.com/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx
//public static K<U, Answer> Bind<T, U, Answer>(this K<T, Answer> m, Func<T, K<U, Answer>> k)
//return (Func<U,Answer> c) => m((T x) => k(x)(c));
// A SelectMany verision of the bind, to use with linq query syntax
public static Action<Action<V>> SelectMany<T, U, V>(this Action<Action<T>> m, Func<T, Action<Action<U>>> k, Func<T, U, V> s)
{
//return m.SelectMany(x => k(x).SelectMany(y => s(x, y).ToContinuation<V, Answer>()));
return Bind(m, x => Bind(k(x), (y => Unit<V>(s(x, y)))));
}
// The OO version of building a continuation from APM
public static IContinuable<T> FromApm<T>(Func<System.AsyncCallback, object, System.IAsyncResult> beginFunc,
Func<System.IAsyncResult, T> endFunc)
{
return new ApmContinuable<T>(beginFunc, endFunc);
}
// And its functional counterpart
public static Action<Action<T>> ContFromApm<T>(Func<System.AsyncCallback, object, System.IAsyncResult> beginFunc,
Func<System.IAsyncResult, T> endFunc)
{
Action<Action<T>> ret = delegate(Action<T> args)
{
var callback = new System.AsyncCallback(delegate(IAsyncResult iar)
{
var retval = endFunc(iar);
args(retval);
});
beginFunc(callback, null);
};
return ret;
}
//public static K<T, Answer> ToContinuation<T, Answer>(this T value)
//return (Func<T, Answer> c) => c(value);
public static Action<Action<T>> Unit<T>(T value)
{
return (Action<T> c) => c(value);
}
// OO version is simple, because we made event implement directly IContinuable.
// It is not even necessary to call FromEvent, it just works
public static IContinuable<T> FromEvent<T>(P.Evt<T> evt)
{
return evt;
}
// The functional version
public static Action<Action<T>> FakeSync<T>(this P.Evt<T> evt)
{
return (Action<T> c) => evt.Async(c);
}
// Let, F# style
public static Action<Action<U>> Let<T, U>(T e, Func<T, Action<Action<U>>> rest)
{
return (Action<U> args) =>
{
var res = rest(e);
res(args);
};
}
//Let -> select? Which relationship?
public static Action<Action<U>> Select<U, T>(this Action<Action<T>> m, Func<T, U> k)
{
return c => m(x => c(k(x)));
}
}
public class BindContinuables<T, U> : IContinuable<U>
{
private IContinuable<T> e;
private Func<T, IContinuable<U>> rest;
public BindContinuables(IContinuable<T> e, Func<T, IContinuable<U>> rest)
{
this.e = e;
this.rest = rest;
}
// just a test.. does this Pure attribute really do something??
//[Pure] .NET 4
public void ContinueWith(Action<U> args)
{
Action<T> cont = delegate(T a)
{
var res = rest(a);
res.ContinueWith(args);
};
e.ContinueWith(cont);
}
}
public class LetContinuables<T, U> : IContinuable<U>
{
private T e;
private Func<T, IContinuable<U>> rest;
public LetContinuables(T e, Func<T, IContinuable<U>> rest)
{
this.e = e;
this.rest = rest;
}
public void ContinueWith(Action<U> args)
{
var res = rest(e);
res.ContinueWith(args);
}
}
//member b.Combine(p1,p2) = bindA p1 (fun () -> p2)
//public void ContinueWith(Action<T> args)
// {
// Func<IContinuable<T>> rest = delegate() { return two; };
// Action<T> cont = delegate(T a)
// {
// var res = rest();
// res.ContinueWith(args);
// };
// one.ContinueWith(cont);
// }
public class SequenceContinuables<T> : IContinuable<T>
{
private IContinuable<T> one;
private IContinuable<T> two;
public SequenceContinuables(IContinuable<T> one, IContinuable<T> two)
{
this.one = one;
this.two = two;
}
public void ContinueWith(Action<T> args)
{
Action<T> cont = delegate(T a)
{
two.ContinueWith(args);
};
one.ContinueWith(cont);
}
}
public class ApmContinuable<T> : IContinuable<T>
{
private Func<System.AsyncCallback, object, System.IAsyncResult> beginFunc;
private Func<System.IAsyncResult, T> endFunc;
public ApmContinuable(Func<System.AsyncCallback, object, System.IAsyncResult> beginFunc,
Func<System.IAsyncResult, T> endFunc)
{
this.beginFunc = beginFunc;
this.endFunc = endFunc;
}
public void ContinueWith(Action<T> args)
{
var callback = new System.AsyncCallback(delegate (IAsyncResult iar) {
var retval = endFunc(iar);
args(retval);
});
beginFunc(callback, null);
}
}
// Someone that has to wait for anyone, but can just "schedule to continue"
// This is the "return" of Haskell and F#, or the "unit" operation for monads
public class UnitContinuable<T> : IContinuable<T>
{
private T a;
public UnitContinuable() { }
public UnitContinuable(T a) { this.a = a; }
public void ContinueWith(Action<T> args)
{
args(a);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment