SodiumでFunctional Reactive Programming (F#) --- 現在値の参照

現在値を参照しない例

Streamの入力があると値が100となる、初期値0のCellは以下のように記述できます。

let value =
    input
    |> Stream.mapTo 100
    |> Stream.hold 0


現在値を参照する例

Streamの入力があると1ずつ増加する、初期値0のCellは以下のように記述できます。

loopWithNoCapturesを利用すると自分自身を参照できるようになります。

snapshotを利用すると、Streamの入力と、その時のCellの値から新しいStreamを作成できます。

let value = Cell.loopWithNoCaptures(fun value ->
    input
    |> Stream.mapTo 1 
    |> Stream.snapshot (+) value
    |> Stream.hold 0 )         


アプリケーション例

上記をふまえてカウンタを作成します。
f:id:any-programming:20170130093248p:plain

実装

DataContextは以下の記事のものを利用しています。
SodiumでFunctional Reactive Programming (F#) --- WPF連携 - 何でもプログラミング

F#でWPFを利用する方法は下記記事を参照してください。
F#でWPF --- ウィンドウ表示 - 何でもプログラミング

Xaml

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Title="MainWindow" Height="100" Width="215">
    <Grid>
        <Button Content="-" Command="{Binding Decrement}" HorizontalAlignment="Left" Margin="46,23,0,0" VerticalAlignment="Top" Width="25"/>
        <TextBlock Text="{Binding Count}" HorizontalAlignment="Left" Margin="97,25,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
        <Button Content="+" Command="{Binding Increment}" HorizontalAlignment="Left" Margin="132,23,0,0" VerticalAlignment="Top" Width="25"/>
    </Grid>
</Window>

F#

type Input = 
    { Decrement : unit Stream 
      Increment : unit Stream }

type Output = { Count : int Cell }

let createOutput input = 
    let count = Cell.loopWithNoCaptures(fun count ->
        Stream.orElseAll [
            input.Decrement |> Stream.mapTo -1 
            input.Increment |> Stream.mapTo 1 ]
        |> Stream.snapshot (+) count
        |> Stream.hold 0 )         
    { Count = count }

[<STAThread>]
[<EntryPoint>]
let main argv = 
    let window = Application.LoadComponent(Uri("MainWindow.xaml", UriKind.Relative)) :?> Window
    window.DataContext <- DataContext(createOutput)
    Application().Run(window) |> ignore    
    0