Reactive¶
背景¶
ここでは MoNo.FSharp.dll に定義されている Reactive の機能について説明します。
WPF は Data Binding という優れた機構を備えています。 MoNo.RAIL の Reactive を利用すると、Data Binding をより手軽に行うことが出来るようになります。
ただし、Reactive は WPF に依存したライブラリではありません。 WPF とは無関係に動かすことが可能です。
ここでは LINQPad を利用して Reactive の動きを確認していきます。
準備¶
LINQPad の [Query | References and Properties] メニューから設定ダイアログを開き、下記の設定をします。
- Additional References タブの Add ボタンで MoNo.FSharp.dll を参照に追加します
- Additional Namespace Imports タブに MoNo namespace を追加します
Reactiveの挙動¶
Reactiveの挙動を確認するために以下のコードを実行してみましょう。
let x = Reactive 0
x |> Observable.add (printfn "%d")
x.Val <- 1
x.Val <- 2
次のような出力が確認できると思います。
0
1
2
値が変更されるとObservable.addで渡されたハンドラにその値が渡されていることが確認できます。 また、変更前の0が出力されていることにも注意して下さい。Observableにaddした時点での値もハンドラに渡されます。
Observable.add のシグネチャを見て下さい。
Observable.add : ('T -> unit) -> IObservable<'T> -> unit
ハンドラとIObservable<’a>を渡す関数であることがわかります。 つまりReactive<’a>は IObservable<’a> を実装しているのです。
Reactive.map¶
Reactiveモジュールにはmap関数も用意されています。シグネチャを確認してみましょう。
Reactive.map: ('a -> 'b) -> source:#IReactive<'a> -> IReactive<'b>
では実際にReactive.mapによってどのような挙動をするか確認してみます。
let x = Reactive 0
let y = x |> Reactive.map ((*) 2)
y |> Observable.add (printfn "%d")
x.Val <- 1
x.Val <- 2
0
2
4
xの値(Val)を変更するとmapに渡された関数によって2倍に変換された値がy.Valに設定され、さらにその値がObservable.addで指定したハンドラに渡されていることがわかります。
Reactive.bind¶
Reactive変数xとyの値を変更しても常にその和を値として持つようなReactive変数zを作りたいとします。
let x = Reactive 0
let y = Reactive 0
let z = ...// 常にx+yになるReactive変数を作るには?
mapを使って実現できるでしょうか。
let x = Reactive 0
let y = Reactive 0
let z =
x |> Reactive.map (fun x ->
y |> Reactive.map (fun y -> x + y))
z |> Observable.add (printfn "%d") // コンパイルエラー
x.Val <- 1
y.Val <- 2
Observable.addをしている行がコンパイルエラーとなります。 これはzがIReactive<int>型ではなくIReactive<IReactive<int>>型となってしまっているからです。
ここでReactive.bindを使用します。先のコードを以下のように修正して下さい。
let x = Reactive 0
let y = Reactive 0
let z =
x |> Reactive.bind (fun x ->
y |> Reactive.map (fun y -> x + y))
z |> Observable.add (printfn "%d")
x.Val <- 1
y.Val <- 2
出力は以下の通りです。xやyの値を変更するとzの値は常にx+yとなっていることがわかります。
0
1
3
MoNo.RAILには上記のコード相当を記述できるreactiveコンピューテーション式が用意されています。 このコンピューテーション式を使うと前述のコードは以下のように書くことができます。
let x = Reactive 0
let y = Reactive 0
let z =
reactive {
let! x = x
let! y = y
return x + y
}
z |> Observable.add ...
ここではコンピューテーション式の詳細には触れませんが、reactiveコンピューテーション式内部では
- let!によってReactive<’a>型に格納されている’a型のValが取り出され
- returnによって’a型の値がIReactive<’a>のValに格納され返される
と考えて差し支えありません。
Reactiveの構造¶
Reactiveがどのような構造になっているか見てみましょう。
抑えておくべきポイントは以下のとおりです。
- IReactive<’a>ではValはreadonlyですがIReactiveW<’a>では読み書き可能です。
- IObservable<’a>を実装しているためObserver<’a>の監視対象にできます。
- INotifyPropertyChangedを実装しており、Valの変更に対して通知イベントが発火するようになっているためWPFでの双方向バインディングが容易に実現できます。