540 likes | 892 Views
Rx: your prescription to cure asynchronous programming blues. Slides license: Creative Commons Attribution Non-Commercial Share Alike See http :// creativecommons.org/licenses/by-nc-sa/3.0/legalcode. Mission statement. Too hard today….
E N D
Rx: your prescription to cure asynchronous programming blues Slides license: Creative Commons Attribution Non-Commercial Share Alike See http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode
Mission statement Too hard today… Rx is a library forcomposing asynchronous and event-basedprograms using observable collections. Queries? LINQ? • Download at MSDN DevLabs • .NET 2.0, .NET 4.0, Silverlight • JavaScript, more…?
Essential InterfacesEnumerables – a pull-based world • interfaceIEnumerable<out T> • { • IEnumerator<T> GetEnumerator();} • interface IEnumerator<out T> : IDisposable • { • boolMoveNext(); • T Current { get; } • void Reset(); • } C# 4.0 covariance You could get stuck
Essential InterfacesEnumerables – a pull-based world (Waiting to move next) C# 4.0 covariance moving on You could get stuck
Mathematical dualityBecause the Dutch are (said to be) cheap • Electricity: inductor and capacitor • Logic: De Morgan’s Law • Programming? Source: http://smartcanucks.cs
What’s the dual of IEnumerable?The recipe to dualization http://en.wikipedia.org/wiki/Dual_(category_theory) Reversing arrows…Input becomes output and vice versa Making a U-turnin synchrony
What’s the dual of IEnumerable?Step 1 – Simpler and more explicit • interfaceIEnumerable<T> • { • IEnumerator<T> GetEnumerator();} • interface IEnumerator<T> : IDisposable • { • boolMoveNext(); • T Current { get; } • void Reset(); • } GetEnumerator(void); MoveNext(void); throwsException; GetCurrent(void); C# didn’t borrow Java checked exceptions
What’s the dual of IEnumerable?Step 1 – Simpler and more explicit • interface IEnumerable<T> • { • } • interface IEnumerator<T> : • { • boolMoveNext(void) throwsException; • T GetCurrent(void); • } IEnumerator<T> GetEnumerator(void); ( & ) We really got an enumerator and a disposable IDisposable
What’s the dual of IEnumerable?Step 2 – Swap input and output IEnumerableDual<T> IEnumerable<T> • interface • { • (IDisposable & );} • interface IEnumerator<T> • { • boolMoveNext(void) throwsException; • T GetCurrent(void); • } Set IEnumerator<T> ) GetEnumerator( void Will onlydualize the synchrony aspect
What’s the dual of IEnumerable?Step 2 – Swap input and output • interfaceIEnumerableDual<T> • { • IDisposableSetEnumerator(IEnumerator<T> x);} • interface IEnumerator<T> • { • boolMoveNext(void) throwsException; • T GetCurrent(void); • } This is an output too
What’s the dual of IEnumerable?Step 2 – Swap input and output • interfaceIEnumerableDual<T> • { • IDisposableSetEnumerator(IEnumerator<T> x);} • interface IEnumerator<T> • { • (bool | Exception) MoveNext(void); • T GetCurrent(void); • } Discrete domain with true and false
What’s the dual of IEnumerable?Step 2 – Swap input and output • interfaceIEnumerableDual<T> • { • IDisposableSetEnumerator(IEnumerator<T> x);} • interface IEnumerator<T> • { • (true | false | Exception) MoveNext(void); • T GetCurrent(void); • } T If you got true, you really got a T
What’s the dual of IEnumerable?Step 2 – Swap input and output • interfaceIEnumerableDual<T> • { • IDisposableSetEnumerator(IEnumerator<T> x);} • interface IEnumerator<T> • { • (T | false | Exception) MoveNext(void); • } void If you got false, you really got void
What’s the dual of IEnumerable?Step 2 – Swap input and output • interfaceIEnumerableDual<T> • { • IDisposableSetEnumerator(} • interface • { • ); • } IEnumeratorDual>); IEnumerator<T> x); IEnumerator<T> IEnumeratorDual<T> Got MoveNext( (T | void | Exception) void But C# doesn’t have discriminated unions… Let’s splat this into three methods instead!
What’s the dual of IEnumerable?Step 2 – Swap input and output • interfaceIEnumerableDual<T> • { • IDisposableSetEnumerator(IEnumeratorDual<T>);} • interface IEnumeratorDual<T> • { • voidGotT(T value); • voidGotException(Exception ex); • voidGotNothing(void);}
What’s the dual of IEnumerable?Step 3 – Consult the Gang of Four… • interfaceIObservable<T> • { • IDisposableSetObserver(IObserver<T> observer);} • interface IObserver<T> • { • voidGotT(T value); • voidGotException(Exception ex); • voidGotNothing(); • } Source:http://amazon.com
What’s the dual of IEnumerable?Step 4 – Variance annotations • interfaceIObservable<out T> • { • IDisposableSetObserver(IObserver<T> observer);} • interface IObserver<in T> • { • voidGotT(T value); • voidGotException(Exception ex); • voidGotNothing(); • } Do you really know C# 4.0? Used to detach the observer…
What’s the dual of IEnumerable?Step 5 – Color the bikeshed (*) • interfaceIObservable<out T> • { • IDisposableSubscribe(IObserver<T> observer);} • interface IObserver<in T> • { • voidOnNext(T value); • voidOnError(Exceptionex); • voidOnCompleted(); • } (*) Visit http://en.wikipedia.org/wiki/Color_of_the_bikeshed
Essential InterfacesObservables – a push-based world • interfaceIObservable<out T> • { • IDisposableSubscribe(IObserver<T> observer);} • interface IObserver<inT> • { • voidOnNext(T value); • voidOnError(Exception ex); • voidOnCompleted(); • } C# 4.0 contravariance You could get flooded
Essential InterfacesSummary – push versus pull Application Got next? MoveNext OnNext Have next! Interactive Reactive Environment IObservable<T> IObserver<T> IEnumerable<T> IEnumerator<T>
demo Essential Interfaces
Getting Your ObservablesPrimitive constructors OnCompleted new int[0] Observable.Empty<int>() OnNext new[] { 42 } Observable.Return(42) OnError Throwing iterator Observable.Throw<int>(ex) Observable.Never<int>() Iterator that got stuck Notion of time
Getting Your ObservablesObserver (and enumerator) grammar OnNext* [OnError|OnCompleted] OnNext(1) OnNext(2) OnNext(0) Observable.Range(0, 3) yield 1 yield 2 yield 0 Enumerable.Range(0, 3)
Getting Your ObservablesGenerator functions A variant with time notion exists (GenerateWithTime) Hypothetical anonymous iterator syntax in C# • o = Observable.Generate( • 0, • i => i < 10, • i => i + 1, • i => i * i • ); • o.Subscribe(x => { • Console.WriteLine(x); • }); e = newIEnumerable<int> { for (int i = 0; i < 10; i++) yield return i * i; }; foreach (var x in e) { Console.WriteLine(x); } Synchronous Asynchronous
Getting Your ObservablesCreate – our most generic creation operator • IObservable<int> o = Observable.Create<int>(observer => { • // Assume we introduce concurrency (see later)… • observer.OnNext(42); • observer.OnCompleted(); • }); • IDisposable subscription = o.Subscribe( • onNext: x => { Console.WriteLine("Next: " + x); }, • onError: ex => { Console.WriteLine("Oops: " + ex); }, • onCompleted: () => { Console.WriteLine("Done"); } • ); C# doesn’t have anonymous interface implementation, so we provide various extension methods that take lambdas. C# 4.0 named parameter syntax
Getting Your ObservablesCreate – our most generic creation operator • IObservable<int> o = Observable.Create<int>(observer => { • // Assume we introduce concurrency (see later)… • observer.OnNext(42); • observer.OnCompleted(); • }); • IDisposable subscription = o.Subscribe( • onNext: x => { Console.WriteLine("Next: " + x); }, • onError: ex => { Console.WriteLine("Oops: " + ex); }, • onCompleted: () => { Console.WriteLine("Done"); } • ); • Thread.Sleep(30000); // Main thread is blocked… F10
Getting Your ObservablesCreate – our most generic creation operator • IObservable<int> o = Observable.Create<int>(observer => { • // Assume we introduce concurrency (see later)… • observer.OnNext(42); • observer.OnCompleted(); • }); • IDisposable subscription = o.Subscribe( • onNext: x => { Console.WriteLine("Next: " + x); }, • onError: ex => { Console.WriteLine("Oops: " + ex); }, • onCompleted: () => { Console.WriteLine("Done"); } • ); • Thread.Sleep(30000); // Main thread is blocked… F10
Getting Your ObservablesCreate – our most generic creation operator • IObservable<int> o = Observable.Create<int>(observer => { • // Assume we introduce concurrency (see later)… • observer.OnNext(42); • observer.OnCompleted(); • }); • IDisposable subscription = o.Subscribe( • onNext: x => { Console.WriteLine("Next: " + x); }, • onError: ex => { Console.WriteLine("Oops: " + ex); }, • onCompleted: () => { Console.WriteLine("Done"); } • ); • Thread.Sleep(30000); // Main thread is blocked… F5
Getting Your ObservablesCreate – our most generic creation operator • IObservable<int> o = Observable.Create<int>(observer => { • // Assume we introduce concurrency (see later)… • observer.OnNext(42); • observer.OnCompleted(); • }); • IDisposable subscription = o.Subscribe( • onNext: x => { Console.WriteLine("Next: " + x); }, • onError: ex => { Console.WriteLine("Oops: " + ex); }, • onCompleted: () => { Console.WriteLine("Done"); } • ); • Thread.Sleep(30000); // Main thread is blocked… Breakpoint got hit
demo Getting Your Observables
Bridging Rx with the WorldWhy .NET events aren’t first-class… How to pass around? Hidden data source • form1.MouseMove+= (sender, args) => { • if (args.Location.X==args.Location.Y) • // I’d like to raise another event • }; • form1.MouseMove -=/* what goes here? */ Lack of composition Resource maintenance?
Bridging Rx with the World…but observables sequences are! Objects can be passed Source of Point values • IObservable<Point>mouseMoves= Observable.FromEvent(frm, "MouseMove"); • varfiltered = mouseMoves • .Where(pos => pos.X == pos.Y); • varsubscription = filtered.Subscribe(…); • subscription.Dispose(); Can define operators Resource maintenance!
Bridging Rx with the WorldAsynchronous methods are a pain… Exceptions? Hidden data source • FileStreamfs = File.OpenRead("data.txt"); • byte[] bs = new byte[1024]; • fs.BeginRead(bs, 0, bs.Length,newAsyncCallback(iar=> { • intbytesRead = fs.EndRead(iar); • // Do something with bs[0..bytesRead-1] • }), • null • ); Really a method pair Cancel? Lack of composition Synchronous completion? State?
Bridging Rx with the World…but observables are cuter! • FileStreamfs = File.OpenRead("data.txt"); • Func<byte[], int, int, IObservable<int>> read =Observable.FromAsyncPattern<byte[], int, int,int>(fs.BeginRead, fs.EndRead); • byte[] bs = new byte[1024]; • read(bs, 0, bs.Length).Subscribe(bytesRead => { • // Do something with bs[0..bytesRead-1]}); Tip: a nicer wrapper can easily be made using various operators
Bridging Rx with the WorldThe grand message • We don’t replace existing asynchrony: • .NET events have their use • the async method pattern is fine too • other sources like SSIS, PowerShell, WMI, etc. • but we… • unify those worlds • introduce compositionality • provide generic operators • hence we… • build bridges!
Bridging Rx with the WorldTerminology: hot versus cold observables • Cold observables • Hotobservables varxs = Observable.Return(42); xs.Subscribe(Console.WriteLine); // Prints 42 xs.Subscribe(Console.WriteLine); // Prints 42 again Triggered by subscription varmme = Observable.FromEvent<MouseEventArgs> (from, "MouseMove"); mme.Subscribe(Console.WriteLine); Mouse events going before subscription
demo Bridging Rx with the World
Composition and QueryingConcurrency and synchronization • What does asynchronous mean? • Greek: • a-syn = not with (independent from each other) • chronos = time • Two or more parties work at their own pace • Need to introduce concurrency • Notion of IScheduler Parameterization of operators varxs = Observable.Return(42, Scheduler.ThreadPool); xs.Subscribe(Console.WriteLine); Will run on the source’s scheduler
Composition and QueryingConcurrency and synchronization • Does duality apply? • Convert between both worlds • “Time-centric” reactive operators: // Introduces concurrency to enumerate and signal… varxs = Enumerable.Range(0, 10).ToObservable(); // Removes concurrency by observing and yielding… varys = Observable.Range(0, 10).ToEnumerable(); Race! source1 source2 source1.Amb(source2)
Composition and QueryingConcurrency and synchronization • How to synchronize? • Compositionality to the rescue! varxs = Observable.Return(42, Scheduler.ThreadPool); xs.Subscribe(x => lbl.Text = "Answer = " + x); • IScheduler • WPF dispatcher • WinForms control • SynchronizationContext xs.ObserveOn(frm).Subscribe(x => lbl.Text = "Answer = " + x);
Composition and QueryingStandard Query Operators • Observables are sources of data • Data is sent to you (push based) • Extra (optional) notion of time • Hence we can query over them // Producing an IObservable<Point> using Select varmme = from mm in Observable.FromEvent<MouseEventArgs>( form, “MouseMove”) selectmm.EventArgs.Location; // Filtering for the first bisector using Where var res = from mm in mme where mm.X == mm.Y select mm;
Composition and QueryingPutting the pieces together Asynchronous request IObservable<string> $$$ TextChanged Dictionary web service React Reaction Reactive Reactor IObservable<DictionaryWord[]> Data bindingon UI thread
Composition and QueryingSelectMany – composition at its best • // IObservable<string> from TextChangedevents • varchanged = Observable.FromEvent<EventArgs>(txt, "TextChanged"); • varinput = (fromtext in changed • select ((TextBox)text.Sender).Text); • .DistinctUntilChanged() • .Throttle(TimeSpan.FromSeconds(1)); • // Bridge with the dictionary web service • var svc = newDictServiceSoapClient();var lookup = Observable.FromAsyncPattern<string, DictionaryWord[]>(svc.BeginLookup, svc.EndLookup); • // Compose both sources using SelectMany • varres = fromterm in input • fromwords inlookup(term) • selectwords; input.SelectMany(term => lookup(term))
demo Composition and Querying
Composition and QueryingAsynchronous programming is hard… React Reactive Reactive| Reacti| Re| React| React| Rea| Reac| | Reactiv| R| input Reactive Reaction Reactive Reactor Service call 1 Service call 2 UI data binding Reactive Reaction Reactive Reactor Source:http://scrapetv.com
Composition and QueryingFixing out of order arrival issues React Reactive Reactive| Reacti| Rea| React| Re| React| Reac| | Reactiv| R| input Cancel call 1 Until Reactive Service call 1 Take Service call 2 UI data binding Reactive Reaction Reactive Reactor
Composition and QueryingApplying the TakeUntil fix • // IObservable<string> from TextChangedevents • varchanged = Observable.FromEvent<EventArgs>(txt, "TextChanged"); • varinput = (fromtext in changed • select ((TextBox)text.Sender).Text); • .DistinctUntilChanged() • .Throttle(TimeSpan.FromSeconds(1)); • // Bridge with the dictionary web service • var svc = newDictServiceSoapClient();var lookup = Observable.FromAsyncPattern<string, DictionaryWord[]>(svc.BeginLookup, svc.EndLookup); • // Compose both sources using SelectMany • varres = fromterm in input • fromwords inlookup(term).TakeUntil(input) • selectwords; Very local fix // Alternative approach for composition using: // IObservable<T> Switch<T>(IObservable<IObservable<T>> sources) varres = (fromtermininput select lookup(term)) .Switch(); Hops from source to source
demo Fixing asynchronous issues
Rx for JavaScript (RxJS) • Parity with Rx for .NET • Set of operators • Taming asynchronous JS • JavaScript-specific bindings • jQuery • ExtJS • Dojo • Prototype • YUI3 • MooTools • Bing APIs
Rx for JavaScript (RxJS) • $("#input").ToObservable("keyUp") • .Throttle(250) • .Select(function(ev) { • return query($(ev.target).text()); • }) • .Switch() • .Select(function(result) { • returnformatAsHtml(result); • }) • .Subscribe( • function(results) { • $("#results").html(results); • }, • function(error) { • $("#results").html("Error: " + error); • });