リアクティブプログラミングに関するウィキペディアの記事を読みました。関数型リアクティブプログラミングに関する小さな記事も読んだ。説明は非常に抽象的なものです。
- 関数型反応プログラミング(FRP)は実際にはどういう意味ですか?
- (非リアクティブプログラミングとは対照的に)リアクティブプログラミングは何で構成されていますか?
私の背景は命令型/オブジェクト指向言語なので、このパラダイムに関連する説明をいただければ幸いです。
リアクティブプログラミングに関するウィキペディアの記事を読みました。関数型リアクティブプログラミングに関する小さな記事も読んだ。説明は非常に抽象的なものです。
私の背景は命令型/オブジェクト指向言語なので、このパラダイムに関連する説明をいただければ幸いです。
回答:
FRPの感触を知りたい場合は、1998年の古いFranチュートリアルから始めることができます。論文については、Functional Reactive Animationから始めて、私のホームページの出版物リンクのリンクとHaskell wikiのFRPリンクをフォローアップしてください。
個人的には、FRPがどのように実装されるかを説明する前に、FRPの意味について考えたいと思います。(仕様のないコードは質問のない答えであり、したがって「間違っていません」。)したがって、Thomas Kが別の答え(グラフ、ノード、エッジ、発射、実行、等)。多くの可能な実装スタイルがありますが、FRP が何であるかを示す実装はありません。
Laurence GのFRPは「「時間をかけて」値を表すデータ型」についてであるという簡単な説明に共鳴します。従来の命令型プログラミングは、これらの動的な値を、状態と変異を介して間接的にのみ取得します。完全な履歴(過去、現在、未来)には、ファーストクラスの表現はありません。さらに、命令パラダイムは時間的に離散的であるため、離散的に進化する値のみを(間接的に)キャプチャできます。対照的に、FRPはこれらの進化する値を直接キャプチャし、継続的に進化する値に問題はありません。
FRPはまた、命令型同時実行を悩ませる理論的および実用的なラットの巣を壊すことなく同時に実行されるという点でも珍しいです。意味的には、FRPの並行性は、きめ細かく、確定的で、継続的です。(私は実装ではなく、意味について話しています。実装には、同時実行性または並列処理が含まれる場合と含まれない場合があります。)セマンティックの決定性は、厳密で非公式な推論にとって非常に重要です。並行性は、命令型プログラミングに非常に複雑さを追加しますが(非決定的インターリーブにより)、FRPでは簡単です。
では、FRPとは何でしょうか。あなた自身がそれを発明したかもしれません。これらのアイデアから始めます。
動的/進化する値(つまり、「経時的」な値)は、それ自体がファーストクラスの値です。それらを定義して結合し、関数に渡したり、関数から渡したりすることができます。私はこれらを「行動」と呼んだ。
動作は、定数(静的)動作や時間(クロックなど)などのいくつかのプリミティブから構築され、その後、順次および並列の組み合わせで構築されます。 n個の動作は、「静的な値に対して」n項関数を「ポイントごと」に、つまり継続的に適用することによって結合されます。
離散現象を説明するには、別のタイプ(ファミリ)の「イベント」を用意します。各イベントには、発生のストリーム(有限または無限)があります。各発生には、関連付けられた時間と値があります。
すべての動作とイベントを構築できる構成的な語彙を考え出すには、いくつかの例を試してください。より一般的/単純な部分に分解し続けます。
しっかりとした地面にいることを確認するには、表示セマンティクスの手法を使用して、モデル全体に構成上の基盤を与えます。これは、(a)各タイプに対応する単純で正確な数学的タイプの「意味」があり、( b)各プリミティブと演算子は、構成要素の意味の関数として単純で正確な意味を持っています。 決して、実装の考慮事項を探索プロセスに混在させないでください。この説明が意味不明な場合は、(a)タイプクラスモーフィズムを使用したDenotational設計、(b)プッシュプル関数型反応プログラミング(実装ビットを無視)、および(c)Denotational Semantics Haskellウィキブックスのページを参照してください。。2つの創設者であるChristopher StracheyとDana Scottから、意味的な意味論には2つの部分があることに注意してください。
これらの原則に固執すれば、FRPの精神で多少なりとも何かが得られると思います。
これらの原則はどこで入手できましたか?ソフトウェア設計では、私はいつも同じ質問をします:「それはどういう意味ですか?」。拡散論的意味論はこの質問の正確なフレームワークを私に与えてくれました、そしてそれは私の美学に合うものです(どちらも私に不満を残す操作的または公理的意味論とは異なります)。だから私は行動とは何かを自問しましたか?命令型計算の時間的に離散した性質は、動作自体の自然な記述ではなく、特定のスタイルのマシンへの適応であることにすぐに気付きました。私が考えることができる最も単純で正確な振る舞いの記述は、単に「(連続的な)時間の関数」なので、それが私のモデルです。嬉しいことに、このモデルは、継続的かつ確定的な並行性を簡単かつ優雅に処理します。
このモデルを正しく効率的に実装することは非常に困難でしたが、それはまた別の話です。
純粋な関数型プログラミングでは、副作用はありません。多くの種類のソフトウェア(たとえば、ユーザーとのやり取りがあるもの)では、ある程度の副作用が必要です。
関数スタイルを維持しながら副作用のような動作を得る1つの方法は、関数型反応型プログラミングを使用することです。これは、関数型プログラミングと反応型プログラミングの組み合わせです。(あなたがリンクしたウィキペディアの記事は後者についてです。)
リアクティブプログラミングの背後にある基本的な考え方は、「時間をかけて」値を表す特定のデータ型があるということです。これらの時間とともに変化する値を含む計算自体は、時間とともに変化する値を持ちます。
たとえば、マウスの座標を時間に対する整数値のペアとして表すことができます。次のようなものがあったとしましょう(これは疑似コードです):
x = <mouse-x>;
y = <mouse-y>;
いつでも、xとyはマウスの座標になります。非リアクティブプログラミングとは異なり、この割り当てを行う必要があるのは1回だけであり、x変数とy変数は自動的に「最新」のままになります。これが、リアクティブプログラミングと関数型プログラミングが非常にうまく連携する理由です。リアクティブプログラミングでは、変数を変更する必要がなくなり、変数の変更で実現できることの多くを実行できます。
次に、これに基づいていくつかの計算を行うと、結果の値も時間とともに変化する値になります。例えば:
minX = x - 16;
minY = y - 16;
maxX = x + 16;
maxY = y + 16;
この例でminX
は、は常にマウスポインターのx座標より16小さくなります。リアクティブ対応ライブラリを使用すると、次のように言うことができます。
rectangle(minX, minY, maxX, maxY)
そして、32x32のボックスがマウスポインターの周りに描画され、どこに移動しても追跡します。
これは、関数型反応型プログラミングに関するかなり良い論文です。
sqrt(x)
、マクロでC を呼び出すと、計算されsqrt(mouse_x())
てdoubleが返されます。真の機能的反応システムでsqrt(x)
は、新しい「時間の経過とともに」が返されます。FRシステムをシミュレートしようとした場合#define
、マクロを優先して変数を断定する必要があります。また、FRシステムは通常、再計算が必要な場合にのみ再計算しますが、マクロを使用すると、部分式に至るまで、常にすべてを再評価することになります。
プログラムがスプレッドシートであり、すべての変数がセルであると想像することは、それがどのようなものかについて最初の直感に到達する簡単な方法です。スプレッドシートのセルのいずれかが変更されると、そのセルを参照するセルも変更されます。FRPと全く同じです。ここで、いくつかのセルが独自に変化する(つまり、外界から取得される)と想像してください。GUIの状況では、マウスの位置が良い例です。
それは必然的にかなり多くを逃します。実際にFRPシステムを使用すると、比喩はかなり速く分解します。1つは、通常、個別のイベントもモデル化しようとする試みです(マウスをクリックするなど)。私はこれをここに置いて、あなたにそれがどのようなものかを考えさせます。
私にとってそれはシンボルの約2つの異なる意味です=
:
x = sin(t)
は、それx
はの別の名前ですsin(t)
。したがって、書き込みx + y
はと同じですsin(t) + y
。関数型反応プログラミングはこの点で数学に似ています。を記述しx + y
た場合、t
使用時の値が何であっても計算されます。x = sin(t)
割り当てである:その手段x
店舗の値 sin(t)
割当時に取ら。x = sin(t)
は平均x
はsin(t)
与えられたの値ですt
。as functionの別名ではありませんsin(t)
。それ以外の場合はそうなりますx(t) = sin(t)
。
2 + 3 = 5
またはa**2 + b**2 = c**2
。
OK、背景知識とあなたが指摘したWikipediaページを読むと、反応型プログラミングはデータフローコンピューティングのようなものですが、特定の外部「刺激」がノードのセットをトリガーしてそれらの計算を実行するようです。
これは、たとえばユーザーインターフェイスコントロール(たとえば、音楽再生アプリケーションの音量コントロール)に触れると、さまざまな表示項目と実際の音声出力の音量を更新する必要があるUI設計に非常に適しています。有向グラフのノードに関連付けられた値の変更に対応するボリューム(スライダーなど)を変更すると、
その「ボリューム値」ノードからのエッジを持つさまざまなノードが自動的にトリガーされ、必要な計算と更新がアプリケーション全体に波及します。アプリケーションはユーザーの刺激に「反応」します。関数型リアクティブプログラミングは、このアイデアを関数型言語で実装するか、または一般に関数型プログラミングパラダイム内で実装するだけです。
「データフローコンピューティング」の詳細については、Wikipediaで、またはお気に入りの検索エンジンを使用して、これら2つの単語を検索してください。一般的な考え方は次のとおりです。プログラムはノードの有向グラフであり、それぞれがいくつかの単純な計算を実行します。これらのノードは、いくつかのノードの出力を他のノードの入力に提供するグラフリンクによって互いに接続されています。
ノードが起動または計算を実行すると、その出力に接続されたノードには、対応する入力が「トリガー」または「マーク」されます。すべての入力がトリガーされた/マークされた/利用可能なすべてのノードが自動的に起動します。グラフは、リアクティブプログラミングの実装方法に応じて、暗黙的または明示的になる場合があります。
ノードは、並列に起動していると見なすことができますが、多くの場合、連続して実行されるか、並列処理が制限されています(たとえば、いくつかのスレッドがそれらを実行している場合があります)。有名な例は、マンチェスターデータフローマシンでした。これは、タグ付きデータアーキテクチャを使用して、1つ以上の実行ユニットを介してグラフ内のノードの実行をスケジュールしました。データフローコンピューティングは、計算をカスケードして非同期にトリガーする計算をトリガーして、実行を1つまたは複数のクロックで制御しようとするよりもうまく機能する状況に非常に適しています。
リアクティブプログラミングは、この「実行のカスケード」のアイデアをインポートし、データフローのような方法でプログラムを考えているようですが、一部のノードが「外界」にフックされ、これらの感覚のときに実行のカスケードがトリガーされます。のようなノードが変更されます。プログラムの実行は、複雑な反射弧に似たものになります。プログラムは、基本的に刺激間で固着している場合とそうでない場合があり、または刺激間で基本的に固着状態に落ち着く場合があります。
「非リアクティブ」プログラミングは、実行の流れと外部入力との関係について非常に異なる見方でプログラミングすることになります。人々はおそらく外部入力に反応する何かを彼らに「反応する」と言いたがるので、それは幾分主観的である可能性が高いです。しかし、本質を見ると、一定の間隔でイベントキューをポーリングし、見つかったイベントを関数(またはスレッド)にディスパッチするプログラムは、反応性が低くなります(一定の間隔でのみユーザー入力に対応するため)。繰り返しますが、これはここでの精神です。高速なポーリング間隔のポーリング実装をシステムに非常に低いレベルで配置し、その上に反応的にプログラミングすることを想像できます。
FRPに関する多くのページを読んだ後、私はようやくFRPに関するこの啓発的な執筆に出くわしました、それは最終的に私はFRPが本当にすべてについて何であるかを理解するようになりました。
ハインリッヒ・アフェルムス(反応性バナナの作者)の下に引用します。
関数型反応型プログラミングの本質は何ですか?
よくある答えは、「FRPはシステムを可変状態ではなく時変関数で表現することだ」であり、それは間違いなく間違いではありません。これが意味論的視点です。しかし、私の意見では、より深く、より満足のいく答えは、次の純粋な構文基準によって与えられます。
関数型反応型プログラミングの本質は、宣言時に値の動的な動作を完全に指定することです。
たとえば、カウンターの例を考えてみましょう。「アップ」および「ダウン」というラベルの付いた2つのボタンがあり、これらを使用してカウンターを増分または減分できます。命令的には、最初に初期値を指定し、次にボタンが押されるたびにそれを変更します。このようなもの:
counter := 0 -- initial value on buttonUp = (counter := counter + 1) -- change it later on buttonDown = (counter := counter - 1)
重要なのは、宣言の時点では、カウンターの初期値のみが指定されているということです。カウンターの動的な動作は、プログラムテキストの残りの部分では暗黙的です。対照的に、関数型反応型プログラミングでは、宣言時に動的な動作全体を次のように指定します。
counter :: Behavior Int counter = accumulate ($) 0 (fmap (+1) eventUp `union` fmap (subtract 1) eventDown)
カウンターのダイナミクスを理解したいときはいつでも、カウンターの定義を見れば十分です。それに起こり得るすべてが右側に表示されます。これは、後続の宣言で以前に宣言された値の動的な動作を変更できる命令型アプローチとは非常に対照的です。
したがって、私の理解では、FRPプログラムは一連の方程式です。
j
離散的です:1,2,3,4 ...
f
依存しt
、これは外部からの刺激をモデル化するためにpossibliltyを取り入れて
プログラムのすべての状態が変数にカプセル化されます x_i
FRPライブラリは、時間の経過、つまりにかかる時間を処理j
しj+1
ます。
これらの方程式については、このビデオでさらに詳しく説明します。
編集:
元の回答から約2年後、最近、FRPの実装には別の重要な側面があるという結論に達しました。彼らは重要な実際的な問題であるキャッシュの無効化を解決する必要があります(通常は解決します)。
x_i
-s の方程式は、依存関係グラフを表します。いくつかの場合にはx_i
時の変更はj
、他のすべてのいないx_i'
時の値j+1
必要が更新される、そうではないすべての依存関係は、いくつかのために再計算する必要があるx_i'
から独立したかもしれませんx_i
。
さらに、x_i
変更を行う-sは増分更新できます。たとえばのは、マップ操作考えるf=g.map(_+1)
スカラ座、中f
とg
しているList
のをInts
。ここf
に対応x_i(t_j)
してg
いますx_j(t_j)
。要素を前に追加すると、のすべての要素に対して操作g
を実行するのは無駄になります。一部のFRP実装(たとえばreflex-frp)は、この問題の解決を目的としています。この問題は、インクリメンタルコンピューティングとも呼ばれます。map
g
つまり、x_i
FRPの動作(-s)は、キャッシュされた計算と考えることができます。x_i
-sの一部がf_i
変更された場合にこれらのキャッシュ(-s)を効率的に無効化および再計算するのはFRPエンジンのタスクです。
j+1
でした。代わりに、連続時間の機能について考えてください。ニュートン、ライプニッツなどが示したように、ODEの積分とシステムを使用してこれらの関数を微分的に、しかし連続的に記述することは、しばしば非常に便利です(そして文字通り「自然」です)。それ以外の場合は、モノ自体ではなく、近似アルゴリズム(および貧弱なアルゴリズム)を記述しています。
Conal Elliottによる紙の単に効率的な機能反応性(直接PDF、233 KB)は、かなり良い紹介です。対応するライブラリも機能します。
この論文は現在、別の論文であるプッシュプル関数型反応プログラミング(直接PDF、286 KB)に取って代わられています。
免責事項:私の答えは、JavaScript用の「リアクティブプログラミング」ライブラリであるrx.jsのコンテキストにあります。
関数型プログラミングでは、コレクションの各アイテムを反復処理する代わりに、高次関数(HoF)をコレクション自体に適用します。したがって、FRPの背後にある考え方は、個々のイベントを処理する代わりに、(observable *で実装された)イベントのストリームを作成し、代わりにそれにHoFを適用することです。このようにして、パブリッシャーをサブスクライバーに接続するデータパイプラインとしてシステムを視覚化できます。
オブザーバブルを使用する主な利点は次のとおりです
。i)コードから状態を抽象化します。たとえば、イベントハンドラーをn番目のイベントごとにのみ発生させる場合や、最初のnイベントの後に発生を停止する場合または、最初の「n」イベントの後にのみ起動を開始する場合は、カウンターの設定、更新、チェックの代わりに、HoF(フィルター、takeUntil、スキップ)を使用できます。
ii)コードの局所性が向上します-コンポーネントの状態を変更する5つの異なるイベントハンドラーがある場合、それらのオブザーバブルをマージし、マージされたオブザーバブルに単一のイベントハンドラーを定義して、5つのイベントハンドラーを1に効果的に組み合わせることができます。システム全体のどのイベントがコンポーネントに影響を与える可能性があるかについては、すべて単一のハンドラーに存在するため、簡単に推論できます。
Iterableは、遅延して消費されるシーケンスです。各項目は、使用するたびにイテレーターによってプルされるため、列挙はコンシューマーによって駆動されます。
オブザーバブルは遅延生成されたシーケンスです。各アイテムはシーケンスに追加されるたびにオブザーバーにプッシュされるため、列挙はプロデューサーによって駆動されます。
おい、これは驚くべき素晴らしいアイデアです!1998年にこのことを知らなかったのはなぜですか。とにかく、これがフランのチュートリアルの私の解釈です。提案は大歓迎です。私はこれに基づいてゲームエンジンを起動することを考えています。
import pygame
from pygame.surface import Surface
from pygame.sprite import Sprite, Group
from pygame.locals import *
from time import time as epoch_delta
from math import sin, pi
from copy import copy
pygame.init()
screen = pygame.display.set_mode((600,400))
pygame.display.set_caption('Functional Reactive System Demo')
class Time:
def __float__(self):
return epoch_delta()
time = Time()
class Function:
def __init__(self, var, func, phase = 0., scale = 1., offset = 0.):
self.var = var
self.func = func
self.phase = phase
self.scale = scale
self.offset = offset
def copy(self):
return copy(self)
def __float__(self):
return self.func(float(self.var) + float(self.phase)) * float(self.scale) + float(self.offset)
def __int__(self):
return int(float(self))
def __add__(self, n):
result = self.copy()
result.offset += n
return result
def __mul__(self, n):
result = self.copy()
result.scale += n
return result
def __inv__(self):
result = self.copy()
result.scale *= -1.
return result
def __abs__(self):
return Function(self, abs)
def FuncTime(func, phase = 0., scale = 1., offset = 0.):
global time
return Function(time, func, phase, scale, offset)
def SinTime(phase = 0., scale = 1., offset = 0.):
return FuncTime(sin, phase, scale, offset)
sin_time = SinTime()
def CosTime(phase = 0., scale = 1., offset = 0.):
phase += pi / 2.
return SinTime(phase, scale, offset)
cos_time = CosTime()
class Circle:
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
@property
def size(self):
return [self.radius * 2] * 2
circle = Circle(
x = cos_time * 200 + 250,
y = abs(sin_time) * 200 + 50,
radius = 50)
class CircleView(Sprite):
def __init__(self, model, color = (255, 0, 0)):
Sprite.__init__(self)
self.color = color
self.model = model
self.image = Surface([model.radius * 2] * 2).convert_alpha()
self.rect = self.image.get_rect()
pygame.draw.ellipse(self.image, self.color, self.rect)
def update(self):
self.rect[:] = int(self.model.x), int(self.model.y), self.model.radius * 2, self.model.radius * 2
circle_view = CircleView(circle)
sprites = Group(circle_view)
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == KEYDOWN and event.key == K_ESCAPE:
running = False
screen.fill((0, 0, 0))
sprites.update()
sprites.draw(screen)
pygame.display.flip()
pygame.quit()
つまり、すべてのコンポーネントを数値のように扱うことができれば、システム全体を数学の方程式のように扱うことができるでしょう。
Paul Hudakの本「The Haskell School of Expression」は、Haskellの入門書であるだけでなく、FRPにもかなりの時間を費やしています。FRPの初心者の方は、FRPのしくみを理解していただくことを強くお勧めします。
この本の新しい書き直し(2011年にリリース、2014年に更新)のように見えるもの、The Haskell School of Musicもあります。
前述のとおり、スプレッドシートのように機能します。通常、イベント駆動型フレームワークに基づいています。
すべての「パラダイム」と同様に、その新しさは議論の余地があります。
アクターの分散フローネットワークの私の経験から、それはノードのネットワーク全体の状態の一貫性の一般的な問題の餌食になる可能性があります。つまり、多くの振動と奇妙なループのトラッピングに終わります。
一部のセマンティクスは参照ループまたはブロードキャストを意味するため、これを回避するのは難しく、アクターのネットワークが予測できない状態に収束する(または収束しない)ため、非常に無秩序になる可能性があります。
同様に、エッジが明確に定義されていても、グローバル状態がソリューションから離れているため、一部の状態に到達しない場合があります。2 + 2は、2が2になったとき、および2がそのままだったかどうかに応じて、4になる場合とされない場合があります。スプレッドシートには、同期クロックとループ検出があります。分散型アクターは通常はそうしません。
すべて楽しい:)。
Clojure subredditで、FRPに関するこの素晴らしいビデオを見つけました。Clojureを知らなくても、簡単に理解できます。
こちらが動画です:http : //www.youtube.com/watch?v=nket0K1RXU4
後半でビデオが参照するソースは次のとおりです。https://github.com/Cicayda/yolk-examples/blob/master/src/yolk_examples/client/autocomplete.cljs
Andre Staltzによるこの記事は、私がこれまでに見た中で最もわかりやすい説明です。
記事からの引用:
リアクティブプログラミングは、非同期データストリームを使用したプログラミングです。
その上で、これらのストリームを結合、作成、およびフィルタリングするための機能の素晴らしいツールボックスが提供されます。
以下は、記事の一部である素晴らしい図の例です。
時間の経過に伴う(または時間を無視した)数学的データ変換についてです。
コードでは、これは機能の純粋さと宣言型プログラミングを意味します。
状態のバグは、標準の命令型パラダイムでは大きな問題です。コードのさまざまなビットにより、プログラムの実行のさまざまな「時点」で共有状態が変更される場合があります。これは対処するのが難しいです。
FRPでは、(宣言型プログラミングのように)データがある状態から別の状態にどのように変換され、何がそれをトリガーするかを記述します。関数は単に入力に反応し、現在の値を使用して新しい値を作成するため、時間を無視できます。これは、状態が変換ノードのグラフ(またはツリー)に含まれ、機能的に純粋であることを意味します。
これにより、複雑さとデバッグ時間が大幅に削減されます。
数学のA = B + CとプログラムのA = B + Cの違いを考えてみてください。数学では、決して変わらない関係を説明しています。プログラムでは、「今」AはB + Cであると述べています。しかし、次のコマンドはB ++になる可能性があります。その場合、AはB + Cと等しくありません。数学または宣言型プログラミングでは、どの時点で質問しても、Aは常にB + Cに等しくなります。
そのため、共有状態の複雑さを排除し、時間の経過とともに値を変更します。あなたのプログラムは推論するのがはるかに簡単です。
EventStreamは、EventStream +いくつかの変換関数です。
Behaviorは、EventStream +メモリ内のいくつかの値です。
イベントが発生すると、変換関数を実行して値が更新されます。これが生成する値は、動作メモリに格納されます。
動作を構成して、他のN個の動作を変換する新しい動作を生成できます。この合成値は、入力イベント(動作)が発生すると再計算されます。
「オブザーバーはステートレスであるため、ドラッグの例のように、ステートマシンをシミュレートするためにオブザーバーが必要になることがよくあります。上記の変数パスなど、関係するすべてのオブザーバーがアクセスできる状態を保存する必要があります。」
引用-オブザーバーパターンの廃止 http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf
リアクティブプログラミングに関する短くて明確な説明がCyclejs-Reactive Programmingに表示されます。シンプルで視覚的なサンプルを使用しています。
[モジュール/コンポーネント/オブジェクト] は事後対応であり、外部イベントに対応することによって自身の状態を管理する責任があります。
このアプローチの利点は何ですか?これは主に[モジュール/コンポーネント/オブジェクト]が自分自身に責任を負うため、コントロールの反転であり、パブリックメソッドに対するプライベートメソッドを使用してカプセル化を改善します。
これは、良い出発点であり、知識の完全なソースではありません。そこから、より複雑で深い論文にジャンプできます。
Rx、.NET用のReactive Extensionsを確認してください。彼らは、IEnumerableを使用すると、基本的にストリームから「プル」することを指摘しています。IQueryable / IEnumerableに対するLinqクエリは、セットから結果を「吸い込む」セット演算です。しかし、IObservableで同じ演算子を使用すると、「反応する」Linqクエリを記述できます。
たとえば、(MyObservableSetOfMouseMovementsのmからmX <100およびmY <100が新しいPoint(mX、mY)を選択する)のようなLinqクエリを記述できます。
そして、Rx拡張機能を使用すれば、それだけです。マウスの動きの着信ストリームに反応して、100、100ボックスにいるときはいつでも描画するUIコードがあります...
FRPは、関数型プログラミング(すべてが機能であるという考えに基づいて構築されたプログラミングパラダイム)とリアクティブプログラミングパラダイム(すべてがストリームであるという考えに基づいて構築された(オブザーバーおよび観察可能な哲学))の組み合わせです。世界最高のはずです。
まず、反応型プログラミングに関するAndre Staltzの投稿をご覧ください。