Reactive Programming

@maruyama097 丸山不二夫

In a recent post on the NetBeans developer site, one of the core maintainer observed that a single class had been patched over 14 times to fix threading related problems. --- Brian Goetz et al “Java Concurrency In Practice”

1/3 of the code in Adobe’s desktop applications is devoted to event handling logic 1/2 of the bugs reported during a product cycle exist in this code --- quoted in Martin Odersky’s “Deprecating the Observer Pattern”

Merriam-Webster defines reactive as “readily responsive to a stimulus”, i.e. its components are “active” and always ready to receive events. This definition captures the essence of reactive applications, focusing on systems that: event-driven, scalable, resilient, responsive ---

“Reactive Manifesto”

はじめに o  Reactive プログラミングの歴史は、インタラク ティブなUIの登場とともに古い。表計算ソフトは 、もっとも身近な最も成功したReactive プログ ラミングの一つである。 o  現代のReactive プログラミングをドライブして いる力の一つは、ResponsiveなUIへの欲求 である。その主要な舞台は、Web UIである。 Web Componentとともに、post HTML5の 中心的なWeb技術の一つである、Model Driven View(MDV)は、こうしたReactive プ ログラミング技術と見ることが出来る。

はじめに o  現代のReactive プログラミングをドライブして いるもう一つの力は、非同期でイベント・ドリブ ンな処理への志向である。この方向は、CPUの メニコア化と分散処理の巨大なクラウドへの拡 大という、まったく異なる二つの分野でともに強 力に押し進められている。 o  2010年頃に公開されたマイクロソフト社の Rx(Reactive Extention)は、Observable の役割に注目し、Reactive プログラミングの 新しい手法を切り開いた画期的なものであった。

はじめに o  Rxは、オープンソース化され、多くの言語に移 植され、また多くのReactive プログラミング言 語に大きな影響を与えた。 o  Rxの応用で注目すべき事例は、Netflixによる RxのJavaへの移植と、それに基づくAPIの書 き換え・システムの再構築の成功である。 o  それはまた、小さな関数から大きな関数を合成 するという関数型のアプローチの、大規模な実 システムでの有効性を示すものでもあった。

Agenda Part I  o  WebとReactiveプログラミング n  n  n  n  n  n 

非同期とイベント・ドリブンへの志向 Web UIとReactiveプログラミング Meteor  AngularJS  Dart Polymer.dart

Agenda Part II o  Reactive Extensionの基礎 n  n  n  n  n  n 

LINQとEnumerable RxとObservable Observableを使ったEvent処理 Rx for JavaScript Observableを使った非同期処理 AndroidとRx

Agenda Part III o  Observableを活用する n  Observableのストリームを処理する n  Observableを変形・加工する合成可能な 関数  n  ServerサイドでのRxの事例 Netflix n  NetflixでのObservableの利用

Agenda  Part IV o  Reactive Manifesto n  The Need to Go Reactive

n  n  n  n  n  n 

Reactive Applications Event-driven Scalable Resilient Responsive Cocclusion

Part I WebとReactiveプログラミング

表計算とReactive Web UI

表計算ソフトはReactive 最も身近で、成功したアプリの一つ

表計算のアルゴリズムを考える Loop { セルCijへの入力/修正のイベント検出   Loop { セルCijへの入力/修正は終了したか? }   セルCijに関連付けられている全てのマクロMnの検索   Loop { 新しいセルCijの値で全てのMnを再計算    }

}

Mnが定義されているセルCklの値を再表示

AngularJSの処理

AngularJSの処理 1.  2. 

3.  4.  5.  6.  7. 

‘X’ keyが押されると、ブラウザーはinputコントロールにkeydow nイベントを発する。 input directiveは、inputの値の変化を捉えて、Angular 実行コ ンテキスト内のアプリケーション・モデルを変更する為に、 $apply(“name = ‘X’;”) を呼び出す。 Angularは、 name = ‘X’; をモデルに適用する。 $digest ループが始まる。 $watch リストは、name属性の変化を検出すると、{{name}} 補完に通知をして、それが、つづいてDOMを更新する。 Angularは実行コンテキストを抜け出す。それで、keydownイベン トは終わり、それとともにJavaScriptの実行コンテキストも終わる。 ブラウザーは、更新されたテキストで、ビューを再描画する。

非同期とイベント・ドリブンへの注目

2009年11月 JSConf

Ryan Dahlの登場 o  ここでは、node.jsの爆発的な拡大の起点となった、 2009年ベルリンで行われた、JSConfでのnode.j sの創始者Ryan Dahlの講演の一部を見てみよう。

“Node.js, Evented I/O for V8 Javascript” by Ryan Dahl

n  http://jsconf.eu/2009/speaker/ speakers_selected.html#entry-3356 n  http://s3.amazonaws.com/four.livejournal/ 20091117/jsconf.pdf

I/O は、違ったやり方で 行われる必要がある。 o  多くのWebアプリは、次のようなコードを使う。 var result = db.query("select * from T"); // use result o  データベースを検索している間、ソフトウェアは 何をしているのだろう? たいていの場合、ただ レスポンスを待っているだけである。 o  それに加えて、IOによる遅延は、コンピュータ の能力と比較すると、次のように、巨大なもの である。 http://s3.amazonaws.com/four.livejournal/20091117/jsconf.pdf

var result = db.query("select..");

o  このようなコードは、全体のプロセスをブロック するか、複数のプロセスの実行をスタックさせる ことになるかのいずれかである。 o  しかし、次のようなコードでは、プログラムは、すぐ にevent loopに戻ることが可能である。どのよ うなメカニズムも必要としない。 db.query("select..", function (result) { // use result });

o  これが、I/Oが行われるべき方法である。 o  では、何故、誰もがevent loopやCallbackや Non-BlokingI/Oを使わないのだろうか?

文化的偏見 o  我々は、I/Oを次のように行うと教えられてきた puts("Enter your name: "); var name = gets(); puts("Name: " + name);

o  我々は、入力を要求されたら、それが終わるま で何もしないと教えられてきた。次のようなコー ドは、複雑すぎるとして、退けられてきた。 puts("Enter your name: "); gets(function (name) { puts("Name: " + name); });

インフラの不在 o  それでは、何故、誰もが event loopを使わな いのだろう? o  シングル・スレッドのevent loopは、I/Oが nonblockingであることを要求するのだが、ほとん どのライブラリーはそうなっていない。 n  POSIX async file I/O は使えない. n  Man pagesは、関数がディスクにアクセスすること があることを触れていない。 (e.g getpwuid()) n  Cに、クロージャーや無名関数がないことが、コール バックを難しくしている。 n  データベースのライブラリーは(e.g. libmysql)、非 同期のサポートをしていない。

“Latency as an Effect” “Principles of Reactive Programming” Coursera lecture by Erik Meijer https://class.coursera.org/reactive-001/ lecture/51



2013 Nov 4th “Principles of Reactive Programming”

https://www.coursera.org/course/reactive

trait Socket { def readFromMemory(): Array[Byte] def sendToEurope(packet: Array[Byte]): Array[Byte] } val socket = Socket() val packet = socket.readFromMemory() val confirmation = socket.sendToEurope(packet)

PCでの処理時間

シーケンシャルな実行時間 val socket = Socket() val packet = socket.readFromMemory()  // 50,000 ナノ秒ブロックする  // 例外が発生しない時には、次の処理へ val confirmation = socket.sendToEurope(packet)  // 150,000,000 ナノ秒ブロックする  // 例外が発生しない時には、次の処理へ

1 ナノ秒を 1 秒だと思うと

1ナノ秒を1秒だとした場合の実行時間 val socket = Socket() val packet = socket.readFromMemory()  // 3日間ブロックする  // 例外が発生しない時には、次の処理へ val confirmation = socket.sendToEurope(packet)  // 5年間ブロックする  // 例外が発生しない時には、次の処理へ

Callback Hell o  “A guide to writing asynchronous javascript programs” http://callbackhell.com/

Asynchronous javascript, or javascript that uses callbacks, is hard to get right intuitively.

o  “Callback hell in nodejs?”

http://stackoverflow.com/questions/18095107/callbackhell-in-nodejs

In below code am I in callbackhell? How to overcome such scenario without using any async modules in pure javascript?

Web UIとReactiveプログラミング

Web UI での Reactive / Model Driven View(MDV)への志向 o  Meteor.js n  Reactive プログラミング n  Reactive-contextとReactive-data-source n  publish/subscribe モデル

o  AngularJS n  Imperativeからdeclarative n  Data binding

o  Polymer.js n  Model Driven View

o  .....

Polymer.dart Observables and Data Binding with Web UI https://www.dartlang.org/web-ui/ observables/

Overview of data binding in Web UI o  Web UI helps you efficiently bind application data to HTML, and vice versa, with observables and observers. o  Observables are variables, fields, or collections that can be observed for changes. o  Observers are functions that run when an observable changes.

Efficiently tracking changes o  Instead of asking every possible observable “Did you change?” on every event loop over and over, Web UI has an efficient mechanism to notify only the right observers at the right time.

Observing variables

Hello Web UI

Web UI is {{ superlative }}



library hello_world; import 'package:web_ui/web_ui.dart'; @observable String superlative = 'awesome'; const List alternatives = const ['wicked cool', 'sweet', 'fantastic', 'wonderful']; int _alternativeCount = 0; String get nextAlternative => alternatives[_alternativeCount++ % alternatives.length]; changeIt() { superlative = nextAlternative; } main() { }

Observing classes

Hello {{person.name}}!

@observable class Person { String name; Person(this.name); } final Person person = new Person('Bob'); const List names = const ['Sally', 'Alice', 'Steph']; newName() { person.name = nextName; }

Observing collections final List timestamps = toObservable([]); void addTimestamp() { timestamps.add(new DateTime.now()); } void clear() { timestamps.clear(); }

Observing nested objects @observable class Person { String name; Address address; } @observable class Address { String city; } @observable Person person;

main() { person = new Person() ..name = 'Clark Kent' ..address = ( new Address() ..city = 'Metropolis' ); }



Expression Observers

The time is

@observable String msg; updateMsg() { msg = new DateTime.now().toString(); } main() { observe(() => msg, (_) { query('#msg').text = msg; }); }

Part II Reactive Extensionの基礎

Reactive Extension (Rx) by Microsoft

http://msdn.microsoft.com/en-us/ data/gg577609.aspx

Reactive Extensionとは? o  “The Reactive Extensions (Rx) is a library to compose asynchronous and event-based programs using observable collections and LINQ-style query operators.” o  「Reactive Extensionは、非同期かつイベント ・ベースのプログラムをオブザーバブル・コレク ションとLINQスタイルの検索操作を利用して 構成する為のライブラリーである。」

講演資料 o  2010年 11月 DevCamp Keynote “Rx: Curing your asynchronous programming blues”

http://channel9.msdn.com/Blogs/codefest/ DC2010T0100-Keynote-Rx-curing-your-asynchronousprogramming-blues

o  2012年 6月 TechEd Europe “Rx: Curing your asynchronous programming blues” http://channel9.msdn.com/Events/TechEd/Europe/2012/ DEV413

Rx オープンソース化と移植 o  2012年11月 MS Open Tech からオープンソ ースとして公開。 Rx.NET :https://github.com/Reactive-Extensions/Rx.NET o  あわせて、JavaScript、C++、Python、Ruby への移植版もオープンソースとして公開。 n  n  n  n 

RxJS : https://github.com/Reactive-Extensions/RxJS RxCpp :https://github.com/Reactive-Extensions/RxCpp RxPy : https://github.com/Reactive-Extensions/RxPy Rx.rb : https://github.com/Reactive-Extensions/Rx.rb

RxJava o  Netflixは、Rx.NETをJavaに移植し、オープン ソースとして公開した。 RxJava: https://github.com/Netflix/RxJava/ o  Netflixは、自社のシステムをRxJavaを用いて 書き換え、大規模システムでのReactive プロ グラミングの有効性を示した。 o  2013年2月 “Functional Reactive in the Netflix API with RxJava” http://techblog.netflix.com/2013/02/rxjavanetflix-api.html

RxJavaの移植によって、JVM上で走る、Clojure, Scala, Groovy, Jruby等の言語でもReactive Extension が走るようになった。

LINQとEnumerable データソースの抽象化として、.NET 3.0か ら導入されたLINQで、中心的な役割を果た すのは、Enumerableインターフェースで ある。 http://code.msdn.microsoft.com/101-LINQSamples-3fb9811b



LINQの最も重要なインターフェース

Enumerable

全てのCollectionクラスは、IEnumerableを実装している interface IEnumerable { IEnumerator Enumerator(); } interface IEnumerator : IDisposable { bool Next(); T Current { get; } void Reset(); }

LINQで利用出来るオペレータ Projection: Select, SelectMany Restriction: Where Partitioning: Take, Skip, TakeWhile, ... Ordering: OrderBy, OrderByDecsending, ... Grouping: GroupBy Set: Distinct, Union, Intersect, .. Conversion: ToList, ToArray, .. Generation: Range, Repeat Aggregate: Count, Sum, Min, Max, Average, ....

LINQは、SQLライクな検索構文で、データソースから データソースを導出することが出来る public void Linq1() { int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; var lowNums = from n in numbers where n < 5 select n; Console.WriteLine("Numbers < 5:"); foreach (var x in lowNums) { Console.WriteLine(x); } }

LINQでは、検索式の中にラムダ式を利用出来る public void Linq5() { string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; var shortDigits = digits.Where( (digit, index) => digit.Length < index);

}

Console.WriteLine("Short digits:"); foreach (var d in shortDigits) { Console.WriteLine( "The word {0} is shorter than its value.", d); }

SelectMany (多段のSelect) public void Linq14() { int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 }; int[] numbersB = { 1, 3, 5, 7, 8 }; var pairs = from a in numbersA from b in numbersB where a < b select new { a, b }; Console.WriteLine("Pairs where a < b:"); foreach (var pair in pairs) { Console.WriteLine( "{0} is less than {1}", pair.a, pair.b); } }

SelectMany (多段のSelect) public void Linq15() { List customers = GetCustomerList(); var orders = from c in customers from o in c.Orders where o.Total < 500.00M select new { c.CustomerID, o.OrderID, o.Total }; ObjectDumper.Write(orders); }

SelectMany (多段のSelect) public void Linq18() { List customers = GetCustomerList(); DateTime cutoffDate = new DateTime(1997, 1, 1); var orders = from c in customers where c.Region == "WA" from o in c.Orders where o.OrderDate >= cutoffDate select new { c.CustomerID, o.OrderID }; ObjectDumper.Write(orders); }

public void Linq41() { string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese" }; var wordGroups = from w in words group w by w[0] into g select new { FirstLetter = g.Key, Words = g };

}

foreach (var g in wordGroups) { Console.WriteLine( "Words that start with the letter '{0}':", g.FirstLetter); foreach (var w in g.Words) Words that start with the letter 'b': blueberry { banana Console.WriteLine(w); Words that start with the letter 'c': chimpanzee } cheese } Words that start with the letter 'a': abacus apple

Reactive Extensionと Observable Rxで中心的な役割を果たすのは、イベント のソースを抽象化したObservableである。 それは、データソースの抽象化として、LIN Qで中心的な役割を果たすEnumerableと よく似ている。

ObservableとObserver o  Observableは、Enumerableと同様に、デー タの集まり。それは相互に変換可能。 o  Observableには時間の概念が入っている。 それは時間とともにデータを生み出す。イベント の抽象に用いられる時に便利である。 o  データ・ソースとしてのObservableが生成す るデータは、ObservableにObserverが Subscribeした時に、はじめて利用出来る。 o  データはObservableからSubscribeの時に 指定したObserver上の関数に自動的に送り出 される。(Push)

Observer上の三つの関数

OnNext, OnError, OnComplete o  ObservableからObserverへデータがpush される時に、次の三つの関数のみが利用される。 n  OnNext(): 通常のデータの受け渡し。 n  OnError(): エラーが発生したこと伝える。 n  OnComplete(): 送るべきデータが終了したこと を伝える。

o  これらの関数の定義は、Observableへの Subscribeの際に与えられねばならない。 o  ObservableへのSubscribeは、Subscriptio nを返す。この値は、Sunscribeをやめる時に 利用される。

EnumarableとObservable Enumerable

どちらもデータの集りである

Observable

時間の流れ

EnumerableとObservableは、 相互に変換出来る Enumerable  à  Observable   .ToObservable();  

Observable  à  Enumerable   .ToEnumerable()

Rxの最も重要なインターフェース Observable interface IObservable { IDisposable Subscribe(IObserver observer); } interface IObserver { void OnNext(T value); void OnError(Exception ex); void OnCompleted(); }

Observerが 実装する3つの タイプのメソッド

ObservableからObserverへの通知 OnNext (42)

OnNext (43)

OnNext (“Hello”)

OnCompleted

OnError (error)

OnNext* (OnError | OnCompleted)?

// Range()は、次のようなObservablを生成する // -1-2-3-4-5-6-7-8-9-10-| IObservable source = Observable.Range(1, 10); // ここでは、Subscribe()は、三つの引数を取っている。 // 順番に、OnNext,OnError,OnCompleteの定義である IDisposable subscription = source.Subscribe( x => Console.WriteLine("OnNext: {0}", x), ex => Console.WriteLine("OnError: {0}", ex.Message), () => Console.WriteLine("OnCompleted") );

Obser verが 実装する 3つの タイプの メソッド

Console.WriteLine("Press ENTER to unsubscribe..."); Console.ReadLine(); // Subscribeをやめる subscription.Dispose();

internal class Generate_Simple { private static void Main() { var observable = Observable.Generate(1, // 初期値 -1-2-3-4-5-| x => x < 6, // 条件 x => x + 1, // 増分 x => x, // 返り値 x=>TimeSpan.FromSeconds(1)).Timestamp(); // ここでは、SubscribeにはOnNextの定義だけが与えられている using (observable.Subscribe(x => Console.WriteLine( "{0}, {1}", x.Value, x.Timestamp))) { Console.WriteLine("Press any key to unsubscribe"); Console.ReadKey(); }

} }

Console.WriteLine("Press any key to exit"); Console.ReadKey();

class Select_Simple { static void Main() {       // 一秒間隔で数列を生成する var oneNumberPerSecond = Observable. Interval(TimeSpan.FromSeconds(1)); var numbersTimesTwo = // 新しいObservableの生成 from n in oneNumberPerSecond select n * 2; // LINQが使える!

}

}

Console.WriteLine("Numbers * 2:"); numbersTimesTwo.Subscribe(num => { Console.WriteLine(num); } // OnNextの定義が引数で与えられている ); Console.ReadKey();

MoveNex t

IEnumerable IEnumerator

Environment

Have next!

IObservable IObserver

Reactive

Got next?

OnNext

Interactive

Application

Observableを使ったEvent処理

マウスイベントをObservableにする

マウスは、イベントのデータベース! var lbl = new Label(); var frm = new Form { Controls = { lbl } }; var moves = Observable. FromEvent(frm, "MouseMove"); using (moves.Subscribe (evt => { lbl.Text = evt.EventArgs.Location.ToString(); })) { Application.Run(frm); }

var txt = new TextBox(); var frm = new Form { Controls = { txt } }; var moves = Observable.FromEvent (frm, "MouseMove"); var input = Observable.FromEvent (txt, "TextChanged"); var movesSubscription = moves.Subscribe( evt => { Console.WriteLine("Mouse at: " + evt.EventArgs.Location); }); var inputSubscription = input.Subscribe( evt => { Console.WriteLine("User wrote: " + ((TextBox)evt.Sender).Text); }); using (new CompositeDisposable( movesSubscription, inputSubscription)) { Application.Run(frm); }

var moves = from evt in Observable. FromEvent(frm, "MouseMove") select evt.EventArgs.Location; var input = from evt in Observable. FromEvent(txt, "TextChanged") select ((TextBox)evt.Sender).Text; var movesSubscription = moves.Subscribe( pos => Console.WriteLine("Mouse at: " + pos)); var inputSubscription = input.Subscribe( inp => Console.WriteLine("User wrote: " + inp));

var overFirstBisector = from pos in moves where pos.X == pos.Y select pos; var movesSubscription = overFirstBisector.Subscribe( pos => Console.WriteLine("Mouse at: " + pos));

違った値が来た時のみデータを送る var input = (from evt in Observable.FromEvent(txt, "TextChanged") select ((TextBox)evt.Sender).Text) .DistinctUntilChanged(); Doは、Observablを変化させずに、OnNextを実行する var input = (from evt in Observable.FromEvent(txt, "TextChanged") select ((TextBox)evt.Sender).Text) .Do(inp => Console.WriteLine("Before DistinctUntilChanged: " + inp)) .DistinctUntilChanged(); Throttleは指定された時間の間、Observableのイベントを抑制する TimeSpan.FromSecond(1)は、一秒間の指定 var input = (from evt in Observable.FromEvent(txt, "TextChanged") select ((TextBox)evt.Sender).Text) .Throttle(TimeSpan.FromSeconds(1)) .DistinctUntilChanged();

using (input.ObserveOn(lbl).Subscribe(inp => lbl.Text = inp)) Application.Run(frm);

Reactive Extension for JavaScript

var source = null; // Observableとしてsourceを生成するコード(省略) var subscription = source.Subscribe( // 三つの関数の定義が順番に引数に与えられている function (next) { $("

").html("OnNext: " + next) .appendTo("#content"); }, function (exn) { $("

").html("OnError: " + exn) .appendTo("#content"); }, function () { $("

").html("OnCompleted") .appendTo("#content"); } );

var source = Rx.Observable.Empty(); OnCompleted var source = Rx.Observable.Throw("Oops!"); OnError: Oops var source = Rx.Observable.Return(42); OnNext: 42 OnCompleted var source = Rx.Observable.Range(5,3); OnNext: 5 OnNext: 6 OnNext: 7 OnCompleted

var source = Rx.Observable.Generate( 0, function (i) { return i < 4; }, function (i) { return i + 1; }, // Like a for loop function (i) { return i * i; } ); OnNext: 0 OnNext: 1 OnNext: 4 OnNext: 9 OnCompleted

RxJS: DOMイベントをObservableに

$(document).ready(function () { $(document).mousemove(function (event) { // A position tracking mechanism, // updating a paragraph called “content” $("

") .text("X: " + event.pageX + " Y: " + event.pageY) .appendTo("#content"); }); jQuery });

RxJS $(document).ready(function () { $(document).toObservable("mousemove") .Subscribe(function (event) { // A position tracking mechanism, // updating a paragraph called “content” $("

") .text("X: " + event.pageX + " Y: " + event.pageY) .appendTo("#content"); }); });

$(document).ready(function () { $(document).toObservable("mousemove") .Subscribe(function (event) { $("

") .text("X: " + event.pageX + " Y: " + event.pageY) .appendTo("#content"); }); $("#textbox").toObservable("keyup") .Subscribe(function (event) { $("

") .text("User wrote: " + $(event.target).val()) .appendTo("#content"); }); });

var moves = $(document).toObservable("mousemove") .Select(function(event) { return { pageX : event.pageX, pageY : event.pageY }; }); var inputs = $(“#textbox”).toObservable(”keyup”) .Select(function(event) { return $(event.target).val(); });

var movesSubscription = moves.Subscribe( function (pos) { $("

”) .text("X: " + pos.pageX + " Y: " + pos.pageY) .appendTo("#content"); }); var inputSubscription = input.Subscribe( function (text) { $("

") .text("User wrote: " +text) .appendTo("#content"); });

var overFirstBisector = moves .Where(function(pos) { return pos.pageX === pos.pageY; }); var movesSubscription = overFirstBisector .Subscribe(function (pos) { $("

") .text("Mouse at: "+pos.pageX+","+pos.pageY) .appendTo("#content"); });

var inputs = $(“#textbox”).toObservable(”keyup”) .Select(function(event) { return $(event.target).val(); } .DistinctUntilChanged(); ); var inputSubscription = input.Subscribe( function (text) { $("

") .text("User wrote: " +text) .appendTo("#content"); });

var inputs = $(“#textbox”).toObservable(”keyup”) .Select(function(event) { return $(event.target).val(); } .Throttle(1000) .DistinctUntilChanged(); ); var inputSubscription = input.Subscribe( function (text) { $("

") .text("User wrote: " +text) .appendTo("#content"); });

Functional Reactive Programming on Android With RxJava http://mttkay.github.io/blog/ 2013/08/25/functional-reactiveprogramming-on-android-withrxjava/

現在のAsyncTaskを使った処理 class DownloadTask extends AsyncTask { protected File doInBackground(String... args) { final String url = args[0]; try { byte[] fileContent = downloadFile(url); File file = writeToFile(fileContent); return file; } catch (Exception e) { // ??? } } protected void onPostExecute(File file) { Context context = getContext(); // ??? Toast.makeText(context, "Downloaded: " + file.getAbsolutePath(), Toast.LENGTH_SHORT) .show(); } }

private Observable downloadFileObservable() { return Observable.create(new OnSubscribeFunc() { @Override public Subscription onSubscribe( Observer fileObserver) {

try { byte[] fileContent = downloadFile(); File file = writeToFile(fileContent); fileObserver.onNext(file); fileObserver.onCompleted(); } catch (Exception e) { fileObserver.onError(e); }

}

} });

return Subscriptions.empty();

class MyFragment extends Fragment implements Observer { private Subscription subscription; @Override

protected void onCreate( Bundle savedInstanceState) { subscription = AndroidObservables .fromFragment(this,downloadFileObservable()) .subscribeOn(Schedulers.newThread()) .subscribe(this); } private Observable downloadFileObservable() { /* as above */ } @Override protected void onDestroy() { subscription.unsubscribe(); }

Observable filePathObservable = downloadFileObservable().map(new Func1() { @Override public String call(File file) { return file.getAbsolutePath(); } }); // now emits file paths, not `File`s subscription = filePathObservable.subscribe(                    /* Observer */);

public void onNext(File file) { Toast.makeText(getActivity(), "Downloaded: " + file.getAbsolutePath(), Toast.LENGTH_SHORT) .show(); } public void onCompleted() {}

}

public void onError(Throwable error) { Toast.makeText(getActivity(), "Download failed: " + error.getMessage(), Toast.LENGTH_SHORT) .show(); }

RxJava on Android with Scala => AWESOME http:// pommedeterresautee.blogspot.jp/ 2013/11/rxjava-on-android-withscala-awesome.html

def getAsyncUrl(urlString: String) :Observable[Option[String]] = Observable { (observer: Observer[Option[String]]) => { val url = new URL(urlString) val urlCon = url.openConnection() urlCon.setConnectTimeout(5000) urlCon.setReadTimeout(5000) val io = Source.fromInputStream( urlCon.getInputStream) val result = Option(io.getLines(). mkString.split("\n")(0)) io.close() observer.onNext(result) observer.onCompleted() Subscription() } }

val mUnsubscribe = CompositeSubscription() mUnsubscribe += getAsyncUrl(lastScript) .execAsync.subscribe( _ match { case OnNext(Some(t)) => CInfo(t) case OnNext(None) => CAlert(getString( R.string.connection_error_message, "Server error")) case OnError(e) => CAlert(getString(R.string.connection_ error_message, e.getLocalizedMessage)) } )

Reactive extensions for .NET (Rx .NET) for Windows Phone http://msdn.microsoft.com/en-us/ library/windowsphone/develop/ ff431792%28v=vs.105%29.aspx

Part III Observableを活用する

Observableのストリームを 処理する Rx サンプル

Stock Trade Analysis MSFT 27.01

INTC 21.75

MSFT 27.96

ticks

from  tick  in  ticks  

MSFT 31.21

INTC 22.54

INTC 20.98

MSFT 30.73

Stock Trade Analysis MSFT 27.01

INTC 21.75

MSFT 27.96

MSFT 31.21

INTC 22.54

INTC 20.98

MSFT 30.73

ticks 27. 01

27. 96

30. 73

31. 21

MSFT 21. 75

INTC from  tick  in  ticks  

group  tick  by  tick.Symbol  

22. 54

20. 98

Stock Trade Analysis MSFT 27.01

INTC 21.75

MSFT 27.96

MSFT 31.21

[27.01, 27.96]

[27.96, 31.21]

INTC 22.54

INTC 20.98

MSFT 30.73

ticks [31.21, 30.73]

MSFT [21.75, 22.54]

INTC from  tick  in  ticks   group  tick  by  tick.Symbol  into  company  

from  openClose  in  company.Buffer(2,  1)  

[22.54, 20.98]

Stock Trade Analysis MSFT INTC MSFT Analysis MSFT INTC Stock Trade 27.01 21.75 27.96 31.21 22.54

INTC 20.98

MSFT 30.73

ticks 0.03 4

0.10 4

-0.0 15

MSFT 0.03 6

-0.0 69

INTC

from  tick  in  ticks   group  tick  by  tick.Symbol  into  company   from  openClose  in  company.Buffer(2,  1)  

let  diff  =  (openClose[1]  –  openClose[0])/openClose[0]  

Stock Trade Analysis MSFT 27.01

INTC 21.75

MSFT 27.96

MSFT 31.21

0.03 4

0.10 4

INTC 22.54

INTC 20.98

MSFT 30.73

ticks -0.0 15

MSFT 0.03 6

-0.0 69

INTC

from  tick  in  ticks   group  tick  by  tick.Symbol  into  company   from  openClose  in  company.Buffer(2,  1)   let  diff  =  (openClose[1]  –  openClose[0])/openClose[0]  

where  diff  >  0.1  

Stock Trade Analysis MSFT 27.01

INTC 21.75

MSFT 27.96

MSFT 31.21

INTC 22.54

INTC 20.98

MSFT 30.73

ticks

res Company = MSFT Increase = 0.104

from  tick  in  ticks   group  tick  by  tick.Symbol  into  company   from  openClose  in  company.Buffer(2,  1)   let  diff  =  (openClose[1]  –  openClose[0])  /  openClose[0]   where  diff  >  0.1  

select  new  {  Company  =  company.Key,  Increase  =  diff  }  

Observableを変形・加工する 合成可能な関数を利用する



Composable Functions o  Transform: map, flatmap, reduce, Scan ... o  Filter: take, skip, sample, takewhile, filter ... o  Combine: concat, merge, zip, combinelatest, multicast, publish, cache, refcount ... o  Concurrency: observeon, subscribeon o  Error Handling: onErrorreturn, onErrorResume ...

map

def map[B](f: A⇒B): Observable[B]

filter

def filter(p: A⇒Boolean): Observable[A]

merge

def merge(Observable[A],Obserbal[A]): Observable[A]

flatmap

def flatMap(f: T=>Observable[S]): Observable[S] = { map(f).flatten }

flatmap val xs: Observable[Int] = Observable(3,2,1)

val yss: Observable[Observable[Int]] = xs.map(x => Observable.Interval(x seconds) .map(_=>x).take(2)) val zs: Observable[Int] = yss.flatten() xs: Observable[Int]

yss: Observable [Observable[Int]]] merge zs: Observable[Int]

concat

val xs: Observable[Int] = Observable(3,2,1) val yss: Observable[Observable[Int]] = xs.map(x => Observable.Interval(x seconds) .map(_=>x).take(2)) val zs: Observable[Int] = yss.concat() xs: Observable[Int]

yss: Observable [Observable[Int]]] zs: Observable[Int]

zip

groupBy

def groupBy[K](keySelector: T⇒K) : Observable[(K,Observable[T])]

startWith

def startWith(ss: T*): Observable[T]

reduce

def reduce(f:(T, T) ⇒T): Observable[T]

ServerサイドでのRxの利用 Netflix --- RxJava “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”

RxJava Java 8へのRxの移植 Observable.toObservable("one”,"two”,"three") .take(2) .subscribe( (arg) -> { System.out.println(arg); } );

https://github.com/Netflix/RxJava

Netflix o  Netflixは、北米のインターネットのダウンロード ・トラフィックの33% を占める。 o  Netflixの顧客は、一月に10億時間以上、テレ ビを見ている。 o  APIのトラフィックは、2010年の2000万/一日 から、2013年には 20億/一日に成長した。

一日の Netflix APIへのリクエスト数

o  クライアントのデバイスは、Netflixの二つの主 要なサービスに接続する。第一のサービスは、 ビデオの検索やそのコンテンツの紹介に関係し た機能を提供するNetflix APIである。第二の サービスは、ビデオ・ストリームの再生サービス である。 o  ここでは、ビデオの検索・発見にかかわるNetflix APIについて見ていく。

Rxの発見は、システムの Re-Atchitectureから始まった

ネットワーク・トラフィックは、 APIの改良で、劇的に改善した

9つあったネットワークの呼び出しは 1つになり、WANでのネットワーク 遅延は一回だけになった

クライアントのロジ ックは、サーバに移 され、20以上の呼び 出しが削除された

関連情報 o  Netflix API architecture http://techblog.netflix.com/search/label/api https://speakerdeck.com/benjchristensen/ o  Re-architecture http://techblog.netflix.com/2013/01/ optimizing-netflix-api.html o  Hystrix https://github.com/Netflix/Hystrix

NetflixでのObservableの利用 Netflixでは、ブロックするAPIをObservable を返す非同期のAPIに作り替え、ついで、それら のObservableを返すAPIを組み合わせてサ ービスを構成した。

Observableを作る Observable.create({ observer ‐> try { observer.onNext(new ....)) observer.onCompleted(); } catch(Exception e) { observer.onError(e); } }) Observable create(Func1, Subscription> func)

def Observable getRating(userId, videoId) { return Observable.create({ observer ‐> executor.execute(new Runnable() { def void run() { try { VideoRating rating = //.. do network call . observer.onNext(rating) observer.onCompleted(); } catch(Exception e) { observer.onError(e); } } }) Asynchronous Observable }) } with Single Values

def Observable

Reactive-short.pdf

logic. 1/2 of the bugs reported during a. product cycle exist in this code. --- quoted in Martin Odersky's. “Deprecating the Observer Pattern”. Page 3 of 206 ...

8MB Sizes 7 Downloads 220 Views

Recommend Documents

No documents