SodiumでFunctional Reactive Programming (F#) --- 導入
Sodiumのコンパイル
NuGetではC#版しか取得できませんので、githubよりソースをダウンロードします。
https://github.com/SodiumFRP/sodium
F#内のSodium.slnをビルドするとSodium.dllが作成されます。
またPriority Queue.dllがNuGetから取得されます。
Hello World
コンソールアプリケーションを作成し、参照に先ほどコンパイルした
Sodium.dll
Priority Queue.dll
を追加します。
下記コードを実行するとHelloとWorldが出力されます。
open Sodium [<EntryPoint>] let main argv = let sink = Stream.sink<unit>() let text = sink |> Stream.mapTo "World" |> Stream.hold "Hello" text |> Cell.listen (printfn "%s") |> ignore sink |> Stream.send () 0
CellとStream
Sodiumの基本はCellクラスとStreamクラスからなります。
他のFRPライブラリではEventとBehaviorと呼ばれており、離散値と連続値…といった定義はあるのですが、導入として
Stream | IEventと同じ |
Cell | 変数 |
と捉えても問題ありません。
例えばHello Worldの例では
sinkはsendで発火可能なEvent
textは初期値が"Hello"でsinkの発火で"World"に変わる変数
と解釈することができます。
Hello World解説
let sink = Stream.sink<unit>() // イベント発火用クラス作成 let text = sink |> Stream.mapTo "World" // 信号を"World"に変換 |> Stream.hold "Hello" // 初期値"Hello"のCellに変換 text |> Cell.listen (printfn "%s") // Cellの値をprint(値が変更される毎) |> ignore // 返り値はとりあえず無視 sink |> Stream.send () // イベント発火
IEvent、IObservableとの違い
Cell
IEvent、IObservableはStreamの役割はしてもCellの役割はしません。
ただしReactive Extensionsを導入するとIObservableでもBehaviorSubjectという近めのクラスが利用できます。
Transaction
IEvent、IObservableではTransactionは利用できません。
Transactionの例として二つの値の合計を出力するコードを掲載します。
Transactionがないと
0
10
30
といった出力となりますが、Transactionを利用することにより
0
30
となります。
これによりパフォーマンスを改善したり、片方が過去の値で片方が新しい値といった予期せぬ処理を防ぐことが可能となります。
ちなみにsendは中でTransactionを生成しているので、Streamを分割して後で結合する記述をしても、終端が何回も計算されることは起こりません。
let sink1 = Stream.sink<int>() let sink2 = Stream.sink<int>() let value1 = sink1 |> Stream.hold 0 let value2 = sink2 |> Stream.hold 0 let sum = Cell.lift2 (+) value1 value2 sum |> Cell.listen (printfn "%d") |> ignore Transaction.Run(fun () -> sink1 |> Stream.send 10 sink2 |> Stream.send 20)