Skip to content

Instantly share code, notes, and snippets.

@b0urb4k1
Last active August 29, 2015 14:25
Show Gist options
  • Save b0urb4k1/8528c3172164d33380b0 to your computer and use it in GitHub Desktop.
Save b0urb4k1/8528c3172164d33380b0 to your computer and use it in GitHub Desktop.
using System.Reactive.Subjects;
using System.Reactive.Linq;
using System;
using LanguageExt;
using static LanguageExt.Prelude;
using static Shock.Strategies;
using System.Collections.Generic;
using static Shock.BusinessLogic;
using System.Linq;
using System.Reactive.Disposables;
using System.Linq.Expressions;
using System.Collections;
namespace Shock
{
#region Variable
public sealed class Variable<T> : ISubject<T>, IDisposable
{
private Subject<T> _subject;
private Action _onDispose;
private Option<T> _optional;
public Variable(Variable<T> other)
{
_subject = other._subject;
_onDispose = other._onDispose;
_optional = other._optional;
}
private Variable(Subject<T> subject)
{
_subject = subject;
this.Subscribe(v => _optional = v);
}
public static Variable<T> make(Subject<T> subject) => new Variable<T>(subject);
public IDisposable Subscribe(IObserver<T> observer) => _subject.Subscribe(observer);
public static Variable<T> make(Action onDispose) => make(default(T), onDispose);
public void OnError(Exception error) => _subject.OnError(error);
public void OnCompleted() => _subject.OnCompleted();
public void OnNext(T value) => _subject.OnNext(value);
public static Variable<T> make(T value, Action onDispose) =>
new Variable<T>(new Subject<T>())
{
_onDispose = onDispose
};
private void Dispose(bool disposing)
{
if (disposing)
{
if (_onDispose != null)
_onDispose();
_subject.Dispose();
}
}
public void Dispose()
{
Dispose(true);
}
public T Value { get { return _optional.IfNone(null); } set { _subject.OnNext(value); } }
}
#endregion
#region Combinder
public static class Combiner
{
public static IObservable<Tuple<T1, T2>> and<T1, T2, T3>
( this IObservable<T1> o1
, Tuple<Func<IObservable<T1>, IObservable<T2>, IObservable<Tuple<T1, T2>>>, Func<IObservable<Tuple<T1, T2>>, IObservable<T3>, IObservable<Tuple<T1, T2, T3>>>> fun
, IObservable<T2> o2
) => fun.Item1(o1, o2);
public static IObservable<Tuple<T1, T2, T3>> and<T1, T2, T3>
( this IObservable<Tuple <T1, T2>> o1
, Tuple<Func<IObservable<T1>, IObservable<T2>, IObservable<Tuple<T1, T2>>>, Func<IObservable<Tuple<T1, T2>>, IObservable<T3>, IObservable<Tuple<T1, T2, T3>>>> fun
, IObservable<T3> o2
) => fun.Item2(o1, o2);
public static IObservable<T3> then<T1, T2, T3>(this IObservable<Tuple<T1, T2>> o, Func<Tuple<T1, T2>, T3> fun) =>
o.Select(v => { return fun(v); });
public static IObservable<T4> then<T1, T2, T3, T4>(this IObservable<Tuple<T1, T2, T3>> o, Func<Tuple<T1, T2, T3>, T4> fun) =>
o.Select(v => { return fun(v); });
public static IObservable<T3> then<T1, T2, T3>(this IObservable<Tuple<T1, T2>> o, Func<T1, T2, T3> fun) =>
o.then(tuplize(fun));
public static IObservable<T4> then<T1, T2, T3, T4>(this IObservable<Tuple<T1, T2, T3>> o, Func<T1, T2, T3, T4> fun) =>
o.then(tuplize(fun));
public static IDisposable assign<T>(this IObservable<T> o, ISubject<T> b) => o.Subscribe(b.OnNext);
public static IDisposable assign<T>(this IObservable<T> o, Environment<T> env, string key) => o.Subscribe(env.GetOrCreate(key));
public static Func<Tuple<T1, T2>, T3> tuplize<T1, T2, T3>(Func<T1, T2, T3> fun) =>
t => fun(t.Item1, t.Item2);
public static Func<Tuple<T1, T2, T3>, T4> tuplize<T1, T2, T3, T4>(Func<T1, T2, T3, T4> fun) =>
t => fun(t.Item1, t.Item2, t.Item3);
public static IDisposable then<T>(this Variable<T> v, Action<T> a) => v.Subscribe(a);
}
#endregion
#region Consuming
public class Consuming<T1, T2> : IObservable<Tuple<T1, T2>>, IDisposable
{
readonly IObservable<T1> _observer1;
readonly IObservable<T2> _observer2;
Option<T1> _optional1;
Option<T2> _optional2;
readonly IDisposable _subscription1;
readonly IDisposable _subscription2;
readonly Subject<Tuple<T1, T2>> _subject = new Subject<Tuple<T1, T2>>();
public IObservable<T1> Observer1 => _observer1;
public IObservable<T2> Observer2 => _observer2;
void OnT1(T1 value) =>
match
( _optional2
, Some: v =>
{
_optional2 = Option<T2>.None;
_subject.OnNext(tuple(value, v));
}
, None: () => _optional1 = value
);
void OnT2(T2 value) =>
match
( _optional1
, Some: v =>
{
_optional1 = Option<T1>.None;
_subject.OnNext(tuple(v, value));
}
, None: () => _optional2 = value
);
void OnCompleted() => _subject.OnCompleted();
void OnError(Exception error) => _subject.OnError(error);
public IDisposable Subscribe(IObserver<Tuple<T1, T2>> observer) => _subject.Subscribe(observer);
public Consuming(IObservable<T1> o1, IObservable<T2> o2)
{
_observer1 = o1;
_observer2 = o2;
_subscription1 = _observer1.Subscribe(OnT1, OnError, OnCompleted);
_subscription2 = _observer2.Subscribe(OnT2, OnError, OnCompleted);
}
public void Dispose()
{
_subscription1.Dispose();
_subscription2.Dispose();
_subject.Dispose();
}
}
public class Consuming<T1, T2, T3> : IObservable<Tuple<T1, T2, T3>>, IDisposable
{
readonly IObservable<Tuple<T1, T2>> _observer1;
readonly IObservable<T3> _observer2;
readonly CompositeDisposable _subscriptions;
readonly Subject<Tuple<T1, T2, T3>> _subject = new Subject<Tuple<T1, T2, T3>>();
Option<Tuple<T1, T2>> _optional1;
Option<T3> _optional2;
void OnT1xT2(Tuple<T1, T2> value) =>
match
( _optional2
, Some: v =>
{
_optional2 = Option<T3>.None;
_subject.OnNext(tuple(value.Item1, value.Item2, v));
}
, None: () => _optional1 = value
);
void OnT2(T3 value) =>
match
( _optional1
, Some: v =>
{
_optional1 = Option<Tuple<T1, T2>>.None;
_subject.OnNext(tuple(v.Item1, v.Item2, value));
}
, None: () => _optional2 = value
);
void OnCompleted() => _subject.OnCompleted();
void OnError(Exception error) => _subject.OnError(error);
public IDisposable Subscribe(IObserver<Tuple<T1, T2, T3>> observer) => _subject.Subscribe(observer);
public Consuming(IObservable<Tuple<T1, T2>> o1, IObservable<T3> o2)
{
_observer1 = o1;
_observer2 = o2;
_subscriptions = new CompositeDisposable
( _observer1.Subscribe(OnT1xT2, OnError, OnCompleted)
, _observer2.Subscribe(OnT2, OnError, OnCompleted)
);
}
public void Dispose()
{
_subscriptions.Dispose();
_subject.Dispose();
}
}
#endregion
#region Strategies
public static class Strategies
{
public static Tuple<Func<IObservable<T1>, IObservable<T2>, IObservable<Tuple<T1, T2>>>, Func<IObservable<Tuple<T1, T2>>, IObservable<T3>, IObservable<Tuple<T1, T2, T3>>>> _buffer<T1, T2, T3>() =>
Tuple.Create<Func<IObservable<T1>, IObservable<T2>, IObservable<Tuple<T1, T2>>>, Func<IObservable<Tuple<T1, T2>>, IObservable<T3>, IObservable<Tuple<T1, T2, T3>>>>
( (o1, o2) => o1.CombineLatest(o2, (v1, v2) => tuple(v1, v2))
, (o1, o2) => o1.CombineLatest(o2, (t, v) => tuple(t.Item1, t.Item2, v))
);
public static Tuple<Func<IObservable<T1>, IObservable<T2>, IObservable<Tuple<T1, T2>>>, Func<IObservable<Tuple<T1, T2>>, IObservable<T3>, IObservable<Tuple<T1, T2, T3>>>> _consume<T1, T2, T3>() =>
Tuple.Create<Func<IObservable<T1>, IObservable<T2>, IObservable<Tuple<T1, T2>>>, Func<IObservable<Tuple<T1, T2>>, IObservable<T3>, IObservable<Tuple<T1, T2, T3>>>>
( (o1, o2) => new Consuming<T1, T2>(o1, o2)
, (o1, o2) => new Consuming<T1, T2, T3>(o1, o2)
);
}
#endregion
#region StandardStrategiess
public static class StandardStrategies
{
public static Tuple<Func<IObservable<int>, IObservable<int>, IObservable<Tuple<int, int>>>, Func<IObservable<Tuple<int, int>>, IObservable<int>, IObservable<Tuple<int, int, int>>>> ConsumeInt => _consume<int, int, int>();
public static Tuple<Func<IObservable<float>, IObservable<float>, IObservable<Tuple<float, float>>>, Func<IObservable<Tuple<float, float>>, IObservable<float>, IObservable<Tuple<float, float, float>>>> ConsumeFloat => _consume<float, float, float>();
public static Tuple<Func<IObservable<int>, IObservable<int>, IObservable<Tuple<int, int>>>, Func<IObservable<Tuple<int, int>>, IObservable<int>, IObservable<Tuple<int, int, int>>>> BufferInt => _buffer<int, int, int>();
public static Tuple<Func<IObservable<float>, IObservable<float>, IObservable<Tuple<float, float>>>, Func<IObservable<Tuple<float, float>>, IObservable<float>, IObservable<Tuple<float, float, float>>>> BufferFloat => _buffer<float, float, float>();
}
#endregion
#region FunctionOf
public static class Function
{
public static IObservable<T3> Of<T1, T2, T3, T4>
( Expression<Func<T1, T2, T3>> expr
, IDictionary<string, Variable<T1>> env1
, IDictionary<string, Variable<T2>> env2
, Tuple
< Func<IObservable<T1>, IObservable<T2>, IObservable<Tuple<T1, T2>>>
, Func<IObservable<Tuple<T1, T2>>, IObservable<T4>, IObservable<Tuple<T1, T2, T4>>>
> fun
)
{
var parameterNames = expr.Parameters.Select(p => p.Name).ToArray();
var consume = _consume<T1, T2, T3>();
if (expr.Body is MethodCallExpression)
{
var function = (MethodCallExpression)expr.Body;
var method = function.Method;
var arguments = function.Arguments.Cast<ParameterExpression>().Select(p => p.Name).ToArray();
var x = env1[arguments[0]];
var y = env2[arguments[1]];
return x.and(fun, y).then(t => (T3)method.Invoke(null, new[] { (object)t.Item1, t.Item2 }));
}
throw new Exception("unsuported expression.");
}
public static IObservable<T4> Of<T1, T2, T3, T4>
( Expression<Func<T1, T2, T3, T4>> expr
, IDictionary<string, Variable<T1>> env1
, IDictionary<string, Variable<T2>> env2
, IDictionary<string, Variable<T3>> env3
, Tuple
< Func<IObservable<T1>, IObservable<T2>, IObservable<Tuple<T1, T2>>>
, Func<IObservable<Tuple<T1, T2>>, IObservable<T3>, IObservable<Tuple<T1, T2, T3>>>
> fun
)
{
var parameterNames = expr.Parameters.Select(p => p.Name).ToArray();
var consume = _consume<T1, T2, T3>();
var body = expr.Body;
if (expr.Body is MethodCallExpression)
{
var function = (expr.Body as MethodCallExpression);
var method = (expr.Body as MethodCallExpression).Method;
var arguments = function.Arguments.Cast<ParameterExpression>().Select(p => p.Name).ToArray();
var x = env1[arguments[0]];
var y = env2[arguments[1]];
var z = env3[arguments[2]];
return x.and(fun, y).and(fun, z).then(t => (T4)method.Invoke(null, new[] { (object)t.Item1, t.Item2, t.Item3 }));
}
//TODO conversions
//else if (expr.Body is UnaryExpression && (expr.Body as UnaryExpression).NodeType == ExpressionType.Convert)
//{
// var function = (expr.Body as UnaryExpression);
// var operand = (MethodCallExpression)function.Operand;
// return FunctionOf(expr.Body, env1, env2, retVariable, fun);
// var method = (expr.Body as MethodCallExpression).Method;
// return null;
//}
throw new Exception("unsuported expression.");
}
}
#endregion
#region Environment
public class Environment<T> : IDisposable, IDictionary<string, Variable<T>>
{
private readonly Dictionary<string, Variable<T>> _dictionaryVariables = new Dictionary<string, Variable<T>>();
private readonly Dictionary<string, T> _dictionary = new Dictionary<string, T>();
public ICollection<string> Keys => _dictionaryVariables.Keys;
public ICollection<Variable<T>> Values => _dictionaryVariables.Values;
public int Count => _dictionaryVariables.Count;
public bool IsReadOnly => true;
Variable<T> IDictionary<string, Variable<T>>.this[string key]
{
get { return GetOrCreate(key); }
set { throw new Exception(); }
}
public Variable<T> this[string key] => GetOrCreate(key);
public Environment(Dictionary<string, T> dictionary)
{
_dictionary = dictionary;
}
public Variable<T> GetOrCreate(string key) => GetOrCreate(key, default(T));
public Variable<T> GetOrCreate(string key, T value)
{
if (_dictionaryVariables.ContainsKey(key) == false)
{
_dictionaryVariables[key] = Variable<T>.make
( value
, () =>
{
if (_dictionary.ContainsKey(key))
_dictionary.Remove(key);
if (_dictionaryVariables.ContainsKey(key))
_dictionaryVariables.Remove(key);
}
);
_dictionaryVariables[key].Subscribe(v => _dictionary[key] = v);
}
var result = _dictionaryVariables[key];
return result;
}
private void Dispose(bool disposing)
{
if (disposing)
{
var list = new List<Variable<T>>();
foreach (var k in _dictionaryVariables)
list.Add(k.Value);
list.ForEach(v => v.Dispose());
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public bool ContainsKey(string key)
{
throw new NotImplementedException();
}
public void Add(string key, Variable<T> value)
{
throw new NotImplementedException();
}
public bool Remove(string key)
{
throw new NotImplementedException();
}
public bool TryGetValue(string key, out Variable<T> value)
{
throw new NotImplementedException();
}
public void Add(KeyValuePair<string, Variable<T>> item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(KeyValuePair<string, Variable<T>> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<string, Variable<T>>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<string, Variable<T>> item)
{
throw new NotImplementedException();
}
public IEnumerator<KeyValuePair<string, Variable<T>>> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
#endregion
#region BusinessLogic
public static class BusinessLogic
{
public static float Multiply(float x, float y) => x * y;
public static float Add3(int x, int y, int z) => x + y + z;
}
#endregion
class Program
{
[STAThread]
public static void Main(string[] args)
{
#region databases
var database1 = new Dictionary<string, int>();
database1.Add("x", 0);
database1.Add("y", 0);
database1.Add("z", 0);
var database2 = new Dictionary<string, float>();
database2.Add("v", 0);
database2.Add("sum", 0);
database2.Add("endValue", 0);
#endregion
var strategyFloat = StandardStrategies.BufferFloat;
var strategyInt = StandardStrategies.BufferInt;
//var strategyFloat = StandardStrategies.ConsumeFloat;
//var strategyInt = StandardStrategies.ConsumeInt;
using (var env1 = new Environment<int>(database1))
using (var env2 = new Environment<float>(database2))
using (Function.Of(expr((int x, int y, int z) => Add3(x, y, z)), env1, env1, env1, strategyInt).assign(env2, "sum"))
using (Function.Of(expr((float sum, float v) => Multiply(sum, v)), env2, env2, strategyFloat).assign(env2, "endValue"))
{
#region variables
var x = env1["x"];
var y = env1["y"];
var z = env1["z"];
var v = env2["v"];
var endValue = env2["endValue"];
#endregion
endValue.then(Console.WriteLine);
x.Value = 1;
y.Value = 2;
z.Value = 3;
v.Value = 3.14f;
v.Value = 2.9f;
//z.Value = 5;
//v.Value = 6;
Console.WriteLine("Read from database");
Console.WriteLine(database2["endValue"]);
}
System.Console.ReadKey();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment