|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Text; |
|
using System.Threading; |
|
using System.Threading.Tasks; |
|
|
|
namespace Monads |
|
{ |
|
public interface IChainElement |
|
{ |
|
IChainElement Parent { get; } |
|
void Run(); |
|
} |
|
|
|
public class ChainMonad<T> : IChainElement |
|
{ |
|
T _value; |
|
bool HasValue; |
|
bool HasContinuation; |
|
AutoResetEvent NextEvent; |
|
AutoResetEvent FinishEvent; |
|
|
|
/// <summary> |
|
/// Used to traverse the entity chain to find the first entity. |
|
/// </summary> |
|
public IChainElement Parent { get; private set; } |
|
|
|
public T Value |
|
{ |
|
get { return _value; } |
|
set { _value = value; HasValue = true; } |
|
} |
|
|
|
private ChainMonad() |
|
{ |
|
NextEvent = new AutoResetEvent(false); |
|
FinishEvent = new AutoResetEvent(false); |
|
HasValue = false; |
|
} |
|
|
|
public ChainMonad(T value) |
|
: this() |
|
{ |
|
HasValue = true; |
|
Value = value; |
|
} |
|
|
|
/// <summary> |
|
/// Starts the invocation chain. |
|
/// If Run() is called on an entity that is not the first |
|
/// node in the invocation chain, it traverses back until it finds |
|
/// the first entity that does not already have a computer value. |
|
/// </summary> |
|
public void Run() |
|
{ |
|
if (!HasValue) |
|
{ |
|
StartChain(); |
|
return; |
|
} |
|
|
|
if (!HasContinuation) |
|
FinishEvent.Set(); |
|
else |
|
NextEvent.Set(); |
|
} |
|
|
|
/// <summary> |
|
/// Blocks program flow until the selected entity has completed its Bind() call |
|
/// </summary> |
|
/// <returns>Value from Bind expression</returns> |
|
public T Wait() |
|
{ |
|
Run(); |
|
FinishEvent.WaitOne(); |
|
return Value; |
|
} |
|
|
|
public ChainMonad<U> Bind<U>(Func<T, U> expression) |
|
{ |
|
HasContinuation = true; |
|
ChainMonad<U> next = new ChainMonad<U>(); |
|
next.Parent = this; |
|
Task.Factory.StartNew(() => |
|
{ |
|
NextEvent.WaitOne(); |
|
var result = expression(Value); |
|
next.Value = result; |
|
next.Run(); |
|
}); |
|
|
|
return next; |
|
} |
|
|
|
private void StartChain() |
|
{ |
|
IChainElement parent = this; |
|
while (parent.Parent != null) |
|
parent = parent.Parent; |
|
|
|
parent.Run(); |
|
} |
|
} |
|
|
|
public static class Extension |
|
{ |
|
public static ChainMonad<T> ToMonad<T>(this T val) |
|
{ |
|
return new ChainMonad<T>(val); |
|
} |
|
|
|
public static ChainMonad<U> Bind<T, U>(this T val, Func<T, U> expression) |
|
{ |
|
var monad = new ChainMonad<T>(val); |
|
var next = monad.Bind(expression); |
|
return next; |
|
} |
|
} |
|
} |