読者です 読者をやめる 読者になる 読者になる

SodiumでFunctional Reactive Programming (F#) --- 導入

Sodiumのコンパイル

NuGetではC#版しか取得できませんので、githubよりソースをダウンロードします。
https://github.com/SodiumFRP/sodium

F#内のSodium.slnをビルドするとSodium.dllが作成されます。
またPriority Queue.dllがNuGetから取得されます。
f:id:any-programming:20170127210644p:plain

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

f:id:any-programming:20170127213045p:plain

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)

f:id:any-programming:20170128161457p:plain