Last active
August 29, 2015 14:25
-
-
Save b0urb4k1/8528c3172164d33380b0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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