Created
April 14, 2014 07:21
-
-
Save JohnNilsson/10623861 to your computer and use it in GitHub Desktop.
Reading Push Pull FRP in C#
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
public static class ComparableExtensions | |
{ | |
public static bool IsAfter<T>(this T c1, T c2) where T:IComparable<T> | |
{ | |
return Comparer<T>.Default.Compare(c1,c2) > 0; | |
} | |
public static T Min<T>(this T c1, T c2) where T:IComparable<T> | |
{ | |
return (c1.CompareTo(c2) < 0) ? c1 : c2; | |
} | |
public static T Max<T>(this T c1, T c2) where T : IComparable<T> | |
{ | |
return (c1.CompareTo(c2) > 0) ? c1 : c2; | |
} | |
} | |
/// <summary> | |
/// Some FRP theory from Conal Eliot ( http://conal.net/papers/push-pull-frp/push-pull-frp.pdf ) | |
/// </summary> | |
internal static class FRPSemantics<TTime> where TTime : IComparable<TTime> | |
{ | |
// 2 Functional Reactive Programming | |
// Behavior<TValue> = Func<TTime,TValue> | |
public delegate T Behavior<T>(TTime t); | |
// EventSource<TValue> = IEnumerable<Tuple<TTime,TValue>> | |
static TTime NegativeInfinity; | |
static TTime PositiveInfinity; | |
public struct Event<T> | |
{ | |
public readonly TTime Time; | |
public readonly T Value; | |
public Event(TTime time, T value) | |
{ | |
this.Time = time; | |
this.Value = value; | |
} | |
} | |
// 2.1 Behaviors | |
static Behavior<TTime> Time = t => t; | |
// 2.1.1 Functor | |
// Compare Observable.CombineLatest | |
static Func<Behavior<TArg1>, Behavior<TResult>> Lift<TArg1, TResult>(Func<TArg1, TResult> f) | |
{ | |
return (b1) => t => f(b1(t)); | |
} | |
static Func<Behavior<TArg1>, Behavior<TArg2>, Behavior<TResult>> Lift<TArg1, TArg2, TResult>(Func<TArg1, TArg2, TResult> f) | |
{ | |
return (b1, b2) => t => f(b1(t), b2(t)); | |
} | |
//... | |
internal static class BehaviorFunctor | |
{ | |
internal static Behavior<TResult> FMap<TArg, TResult>(Behavior<TArg> b, Func<TArg, TResult> f) | |
{ | |
return Lift(f)(b); | |
} | |
} | |
// 2.1.2 Applicative Functor | |
internal static class BehaviorApplicativeFunctor | |
{ | |
static Behavior<T> Pure<T>(T value) | |
{ | |
return _ => value; | |
} | |
static Behavior<TResult> Ap<TArg, TResult>(Behavior<TArg> b, Behavior<Func<TArg, TResult>> f) | |
{ | |
return t => f(t)(b(t)); | |
} | |
} | |
static class Semantic | |
{ | |
public static class Functor<T> | |
{ | |
static Func<T,TResult> FMap<TArg, TResult>(Func<T, TArg> f, Func<TArg, TResult> g) | |
{ | |
return t => g(f(t)); | |
} | |
} | |
public static class ApplicativeFunctor<T> | |
{ | |
static Func<T, TResult> Pure<TResult>(TResult value) | |
{ | |
return _ => value; | |
} | |
static Func<T, TResult> Ap<TArg, TResult>(Func<T, TArg> f, Func<T, Func<TArg,TResult>> g) | |
{ | |
return t => g(t)(f(t)); | |
} | |
} | |
} | |
// 2.2 Events | |
// 2.2.1 Monoid | |
static class EventMergeMonoid<T> | |
{ | |
public static IEnumerable<Event<T>> MEmpty = Enumerable.Empty<Event<T>>(); | |
public static IEnumerable<Event<T>> MAppend(IEnumerable<Event<T>> e1, IEnumerable<Event<T>> e2) | |
{ | |
using (var e1s = e1.GetEnumerator()) | |
using (var e2s = e2.GetEnumerator()) | |
return Merge(e1s, e2s); | |
} | |
private static IEnumerable<Event<T>> Merge(IEnumerator<Event<T>> es1, IEnumerator<Event<T>> es2) | |
{ | |
Event<T>? e1, e2; | |
while(true) | |
{ | |
e1 = es1.MoveNext() ? es1.Current : (Event<T>?)null; | |
e2 = es2.MoveNext() ? es2.Current : (Event<T>?)null; | |
if (e1.HasValue && e2.HasValue) | |
{ | |
if (e1.Value.Time.IsAfter(e2.Value.Time)) | |
{ | |
yield return e2.Value; | |
yield return e1.Value; | |
} | |
else | |
{ | |
yield return e1.Value; | |
yield return e2.Value; | |
} | |
} | |
else if (e1.HasValue) | |
yield return e1.Value; | |
else if (e2.HasValue) | |
yield return e2.Value; | |
else | |
yield break; | |
} | |
} | |
} | |
// 2.2.2 Event functor | |
static class EventFunctor | |
{ | |
public static IEnumerable<Event<TResult>> FMap<TArg,TResult>(IEnumerable<Event<TArg>> es, Func<TArg,TResult> f) | |
{ | |
return es.Select(e => new Event<TResult>(e.Time, f(e.Value))); | |
} | |
} | |
// 2.2.3 Event Monad | |
static class EventMonad | |
{ | |
public static IEnumerable<Event<T>> Unit<T>(T value) | |
{ | |
yield return new Event<T>(NegativeInfinity, value); | |
} | |
public static IEnumerable<Event<T>> Join<T>(IEnumerable<Event<IEnumerable<Event<T>>>> es) | |
{ | |
return es.Select(DelayOccs).Aggregate(EventMergeMonoid<T>.MEmpty, EventMergeMonoid<T>.MAppend); | |
} | |
private static IEnumerable<Event<T>> DelayOccs<T>(Event<IEnumerable<Event<T>>> es) | |
{ | |
return from e in es.Value | |
select new Event<T>(es.Time.Max(e.Time), e.Value); | |
} | |
} | |
// 2.3 Combining behaviors and events | |
static Behavior<T> Switcher<T>(Behavior<T> b0, IEnumerable<Event<Behavior<T>>> es) | |
{ | |
return t => | |
es.TakeWhile(e => t.IsAfter(e.Time)).Select(e => e.Value) | |
.DefaultIfEmpty(b0) | |
.LastOrDefault()(t); | |
} | |
static Behavior<T> Stepper<T>(T v0, IEnumerable<Event<T>> es) | |
{ | |
return t => | |
es.TakeWhile(e => t.IsAfter(e.Time)).Select(e => e.Value) | |
.DefaultIfEmpty(v0) | |
.LastOrDefault(); | |
} | |
// 4. Future | |
struct FutureTime | |
{ | |
TTime LowerBound; | |
Task<TTime> Time; | |
} | |
// 5.2 Reactive values | |
delegate T Reactive<T>(TTime t); | |
//static Reactive<T> Reactive<T>(T value, IEnumerable<Event<T>> events) | |
//{ | |
// var reactive = Stepper(value, events); | |
// return t => reactive(t); | |
//} | |
// 5.3 Time functions | |
delegate T Fun<TTime,T>(TTime t); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment