Fable.Reaction Release 2.1.0

Oct 24, 2020

Contents

1 Fable Reaction 3 1.1 Introduction...... 3 1.2 Getting Started...... 3 1.3 Install...... 5 1.4 Reactive MVU Architecture...... 5 1.5 Examples...... 6 1.6 WebSocket...... 7 1.7 Demo...... 7

2 AsyncRx 9 2.1 Introduction...... 9 2.2 Install...... 9 2.3 Operators...... 10 2.4 Types...... 15 2.5 Disposables...... 17 2.6 Query Builder...... 18 2.7 Subjects...... 19 2.8 Error Handling...... 20 2.9 Custom Observables...... 20

3 Indices and tables 23

Index 25

i ii Fable.Reaction, Release 2.1.0

Fable Reaction is a collection of projects that combines the power of asynchronous reactive (AsyncRx) (RFP) with F#, Fable and Elmish applications.

Contents 1 Fable.Reaction, Release 2.1.0

2 Contents CHAPTER 1

Fable Reaction

Fable Reaction is a library combining reactive programming with the model view update (MVU) style architecture.

1.1 Introduction

Fable.Reaction is a library combining reactive programming with the model view update (MVU) style architecture. Fable.Reaction builds on React Hooks and can perform any task. Think of them as live components that you can include anywhere in your view or be mounted anywhere on the page. Fable.Reaction can easily be used together with Fable.Elmish, but have no dependency on Elmish itself. Views and components built using Fable.Reaction can be included in Elmish views or mounted by themselves anywhere on the page.

1.2 Getting Started

To use Fable.Reaction you need to create an instance of a StreamView. The StreamView works almost the same way as an Elmish application taking an initial model, a view and and update function. In addition it takes a stream function that transforms the stream of dispatched messages before they reach the update function.

open FSharp.Control.AsyncRx// 1. Enable AsyncRx open Fable.Reaction// 2. Enable Fable Reaction

let update (currentModel : Model) (msg : Msg) : Model= ...

let view (model : Model) (dispatch : (Msg-> unit))= ...

let stream model msgs=// 3. Add the reactive stream msgs |> AsyncRx.delay 1000 (continues on next page)

3 Fable.Reaction, Release 2.1.0

(continued from previous page) |> AsyncRx.toStream"msgs" let app= Reaction.StreamView initialModel view update stream mountById"reaction-app" (ofFunction app () [])

1.2.1 Loading initial State

To load initial state from the server without using commands (Cmd) you create an Async Observable using ofPromise and then concat the result into the message stream. Thus the message stream in the example below will start with the initialCountLoaded message.

// Add open statements to top of file open Fable.Reaction let loadCount= ofPromise (fetchAs"//init" []) |> AsyncRx.map (Ok >> InitialCountLoaded) |> AsyncRx.catch (Error >> InitialCountLoaded >> single) |> AsyncRx.toStream"loading" let stream model msgs= match model with | Loading-> loadCount |_-> msgs

1.2.2 Using Fable.Reaction with Elmish

Fable.Reaction can be used all by itself without Elmish. But if you want to use Fable Reaction with Elmish you just add the Fable.Reaction component to the Elmish view like any other element such as e.g div and str. A Fable.Reaction component produces a ReactElement that can be used anywhere in your view such as with the autocomplete component below. let view (model: Model) (dispatch : Dispatch)= Container.container [] [ h1 [] [ str"Search Wikipedia" ] autocomplete { Search=searchWikipedia; Dispatch= Select >> dispatch;

˓→DebounceTimeout=750}

div [ Style [ MarginTop"30px"]][ match model.Selection with | Some selection-> yield str"Selection:" yield str selection | None ->() ] ]

4 Chapter 1. Fable Reaction Fable.Reaction, Release 2.1.0

1.2.3 Doing side effects per message

In the example below we flat map (map and merge) the result of querying Wikipedia back into the message stream. The flatMapLatest operator is a combination of the map and switchLatest operators. This operator works like flatMap but will auto-cancel any ongoing fetch operation if a new query is made before the previous result is ready.

// Add open statements to top of file open Fable.Reaction

let stream model msgs= msgs |> AsyncRx.choose Msg.asKeyboardEvent |> AsyncRx.map targetValue |> AsyncRx.filter (fun term-> term.Length>2|| term.Length=0) |> AsyncRx.debounce 750// Pause for 750ms |> AsyncRx.distinctUntilChanged// Only if the value has changed |> AsyncRx.flatMapLatest searchWikipedia |> AsyncRx.toStream"msgs"

1.3 Install

Fable.Reaction is available as a NuGet package for NETStandard 2.0. Fable.Rection is the new release for Fable ~> 3 and follows the Fable versioning scheme, i.e if you use Fable.Core ~> 3, you should use Fable.Reaction ~> 3.

1.3.1 Paket

> paket add Fable.Reaction

1.3.2 NuGet

> dotnet add package Fable.Reaction

1.4 Reactive MVU Architecture

Fable.Reaction is very similar to Elm and Elmish in regards to the MVU architecture. But when using Fable.Reaction, we do not need any commands (Cmd) or subscriptions with Elmish. Instead we use a ReactiveX (AsyncRx) style query that transforms the stream of messages (Msg).

1.3. Install 5 Fable.Reaction, Release 2.1.0

• Model, application state as immutable data • View, a pure function that takes the model to produce the output view (HTML elements) • Message, a data event that represents a change. Messages are generated by the view, or they may be generated by a stream, e.g. timer or initial message events. • Update, a pure function that produces a new model based on a received message and the previous model In addition to the normal MVU architecture, Fable.Reaction also have a stream that transforms the “stream” of mes- sages going from the view to the update function. • Stream, a function that takes the current model and the message stream and produces a new (transformed) message stream. Note that this also replaces the need for Elm(ish) commands (Cmd) since the stream is free to produce any messages (out of thin air), transform, filter, time-shift messages or combine side-effects such as web requests (fetch) etc.

1.5 Examples

Examples of how to use Fable Reaction can be found in the examples folder. Current list of examples includes: • Counter, from the SAFE stack template. • Timeflies. See description below. • Autocomplete • Dragndrop • Magic The Timeflies example (source code) implements the classic Time Flies) example from RxJS. The Magic example (source code) shows howto scale Fable.Reaction using multiple sub-components.

6 Chapter 1. Fable Reaction Fable.Reaction, Release 2.1.0

1.6 WebSocket

Fable.Reaction enables you to route stream of messages to the server and back again using “message channels”. Note that server side support for WebSocket message handling must also be in place using Reaction. AspNetCoreMiddleWare.

1.6.1 Message Channel

A message channel is an AsyncRx operator that composes a WebSocket stream of messages into the reactive query. This basically routes the messages to the server and (possibly) back again. A message channnel works like this: • Every message that enters the operator will be sent to the server over a WebSocket. • Every message received from the server will be pushed to observers or the next composed operator. This enables us to compose the message channel into the qury without resorting to . val msgChannel<'msg>: uri:string -> encode:('msg -> string) -> decode:(string -> 'msg option) -> source:IAsyncObservable<'msg> -> IAsyncObservable<'msg> Example

let stream (msgs: Stream)= msgs |> AsyncRx.msgChannel"ws://localhost:8085/ws" Msg.Encode Msg.Decode |> AsyncRx.toStream"msgs"

1.7 Demo

1.7.1 Time Flies Like an Arrow

The Timeflies example (source code) implements the classic Time Flies example from RxJS. In the Time files demo the stream of mouse moves are transformed into a stream of letters where each letter is delayed according to its position. The Model type holds data that you want to keep track of while the application is running. type Model={ Letters: Map }

The Msg type defines what events/actions can occur while the application is running. The state of the application changes only in reaction to these events type Msg= | Letter of int * string * int * int

The update function computes the next state of the application based on the current state and the incoming messages let update (msg : Msg) (currentModel : Model) : Model= match currentModel.Letters, msg with | _, Letter (i, c, x, y)-> { currentModel with Letters= currentModel.Letters.Add (i, (c, x, y)) }

1.6. WebSocket 7 Fable.Reaction, Release 2.1.0

The view function renders the model and the result will be handled over to React. It produces a div element and then takes each letter in the Map and adds a span element containing the letter. The span element will have the top/left properties set so that the letter is rendered at the correct position on the page. let view (model : Model) (dispatch : Dispatch)= let letters= model.Letters

div [ Style [ FontFamily"Consolas, monospace"; Height"100%"]] [ [ for KeyValue(i, (c, x, y)) in letters do yield span [ Key (c+ string i); Style [Top y; Left x; Position"fixed

˓→"]] [ strc]]|> ofList ]

The init funciton produces an empty model. let init () : Model= { Letters= Map.empty }

Helper code to render the letters at the correct position on the page. let getOffset (element: Browser.Element)= let doc= element.ownerDocument let docElem= doc.documentElement let clientTop= docElem.clientTop let clientLeft= docElem.clientLeft let scrollTop= Browser.window.pageYOffset let scrollLeft= Browser.window.pageXOffset

int (scrollTop- clientTop), int (scrollLeft- clientLeft) let container= Browser.document.querySelector"#elmish-app" let top, left= getOffset container

Message stream (expression style) that transforms the stream of mouse moves into a stream of letters where each letter is delayed according to its position in the stream. let stream (model : Model) (msgs: IAsyncObservable) = asyncRx { let chars = Seq.toList "TIME FLIES LIKE AN ARROW" |> Seq.mapi (fun i c -> i, c)

let! i, c = AsyncRx.ofSeq chars yield! AsyncRx.ofMouseMove () |> AsyncRx.delay (100 * i) |> AsyncRx.map (fun m -> Letter (i, string c, int m.clientX + i * 10 + 15 ˓→- left, int m.clientY - top)) } |> AsyncRx.tag "msgs"

• search

8 Chapter 1. Fable Reaction CHAPTER 2

AsyncRx

Async Observables for asynchronous and reactive programming.

2.1 Introduction

FSharp.Control.AsyncRx is a library for asynchronous and reactive programming, and is an implementation of Async Observables (ReactiveX) for F# and Fable. FSharp.Control.AsyncRx makes it easy to compose and combine streams of asynchronous event based data such as timers, mouse-moves, keyboard input, web requests and enables you to do operations such as: • Filter • Transform • Aggregate • Combine • Time-shift FSharp.Control.AsyncRx was designed spesifically for targeting Fable which means that the code may be transpiled to JavaScript, and thus the same F# code may be used both client and server side for full stack software development. See /fable.elmish/index for use of FSharp.Control.AsyncRx with Elmish and the model view update (MVU) style architecture.

2.2 Install

FSharp.Control.AsyncRx is available as a NuGet package for NETStandard 2.0.

9 Fable.Reaction, Release 2.1.0

2.2.1 Paket

> paket add FSharp.Control.AsyncRx--project

2.2.2 NuGet

> dotnet add package FSharp.Control.AsyncRx--version 1.0.0

There is a dependency for FSharp.Control.AsyncSeq, but only when used with .NET (not Fable).

2.3 Operators

The following parameterized async observerable returning functions (operators) are currently supported. Other opera- tors may be implemented on-demand, but the goal is to keep it simple and not make this into a full featured ReactiveX implementation (if possible). To use the operators open either the FSharp.Control namespace.

open FSharp.Control

xs= AsyncRx.single 42

You can also open the FSharp.Control.AsyncRx module if you don’t want to prepend every operator with AsyncRx. Be aware of possible namespace conflicts with operators such as map.

open FSharp.Control.AsyncRx

xs= single 42

2.3.1 Creating

Functions for creating ('a -> IAsyncObservable<'a>) an async observable. val empty: : unit -> IAsyncObservable<'a> Returns an async observable sequence with no elements. You must usually indicate which type the resulting observable should be since empty itself doesn’t produce any values.

Example:

let xs= AsyncRx.empty()

10 Chapter 2. AsyncRx Fable.Reaction, Release 2.1.0 val single: x:'a -> IAsyncObservable<'a> Returns an observable sequence containing the single specified element.

Example:

let xs= AsyncRx.single 42 val fail: error:exn -> IAsyncObservable<'a> Returns the observable sequence that terminates exceptionally with the specified exception. Example:

exception MyError of string

let error= MyError"error" let xs= AsyncRx.fail error val defer: factory:(unit -> IAsyncObservable<'a>) -> IAsyncObservable<'a> Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. val create: subscribe:(IAsyncObserver<'a> -> Async) -> IAsyncObservable<'a> Creates an async observable (AsyncObservable<’a>) from the given subscribe function. val ofSeq: seq<'a> -> IAsyncObservable<'a> Returns the async observable sequence whose elements are pulled from the given enumerable sequence. val ofAsyncSeq: AsyncSeq<'a> -> IAsyncObservable<'a> Convert async sequence into an async observable (Not available in Fable). val timer: int -> IAsyncObservable Returns an observable sequence that triggers the value 0 after the given duetime. val interval: int -> IAsyncObservable Returns an observable sequence that triggers the increasing sequence starting with 0 after the given period.

2.3.2 Transforming

Functions for transforming (IAsyncObservable<'a> -> IAsyncObservable<'b>) an async observable. val map: mapper:('a -> 'b) -> source: IAsyncObservable<'a> -> IAsyncObservable<'b> Returns an observable sequence whose elements are the result of invoking the mapper function on each element

2.3. Operators 11 Fable.Reaction, Release 2.1.0

of the source. Example:

let mapper x=x * 10

let xs= AsyncRx.single 42|> AsyncRx.map mapper val mapi: mapper:('a*int -> 'b) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Returns an observable sequence whose elements are the result of invoking the map- per function and incorporating the element’s index on each element of the source.

val mapAsync: ('a -> Async<'b>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Returns an observable sequence whose elements are the result of in- voking the async mapper function on each element of the source.

val mapiAsync: ('a*int -> Async<'b>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Returns an observable sequence whose elements are the result of invoking the async mapper function by incor-

12 Chapter 2. AsyncRx Fable.Reaction, Release 2.1.0

porating the element’s index on each element of the source. val flatMap: ('a -> IAsyncObservable<'b>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Projects each element of an observable sequence into an observable sequence and merges the resulting observ- able sequences back into one observable sequence. val flatMapi: ('a*int -> IAsyncObservable<'b>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Projects each element of an observable sequence into an observable sequence by incorporating the element’s index on each element of the source. Merges the resulting observable sequences back into one observable sequence. val flatMapAsync: ('a -> Async\\>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Asynchronously projects each element of an observable sequence into an observable sequence and merges the resulting observable sequences back into one observable sequence. val flatMapiAsync: ('a*int -> Async\>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Asynchronously projects each element of an observable sequence into an observable sequence by incorporating the element’s index on each element of the source. Merges the resulting observable sequences back into one observable sequence. val flatMapLatest: ('a -> IAsyncObservable<'b>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Transforms the items emitted by an source sequence into observable streams, and mirror those items emitted by the most-recently transformed observable sequence. val flatMapLatestAsync: ('a -> Async\>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Asynchronosly transforms the items emitted by an source sequence into observable streams, and mirror those items emitted by the most-recently transformed observable sequence. val catch: (exn -> IAsyncObservable<'a>) -> IAsyncObservable<'a> -> IAsyncObservable<'a> Returns an observable sequence containing the first sequence’s elements, followed by the elements of the handler sequence in case an exception occurred.

2.3.3 Filtering

Functions for filtering (IAsyncObservable<'a> -> IAsyncObservable<'a>) an async observable. val filter: predicate:('a -> bool) -> IAsyncObservable<'a> -> IAsyncObservable<'a> Filters the elements of an observable sequence based on a predicate. Returns an observ- able sequence that contains elements from the input sequence that satisfy the condition.

Example:

let predicate x=x<3

let xs= AsyncRx.ofSeq<| seq { 1..5}|> AsyncRx.filter predicate

2.3. Operators 13 Fable.Reaction, Release 2.1.0 val filterAsync: ('a -> Async) -> IAsyncObservable<'a> -> IAsyncObservable<'a> Filters the elements of an observable sequence based on an async predicate. Returns an observable sequence that contains elements from the input sequence that satisfy the condition. val distinctUntilChanged: IAsyncObservable<'a> -> IAsyncObservable<'a> Return an observable sequence only containing the distinct contiguous elementsfrom the source sequence. val takeUntil: IAsyncObservable<'b> -> IAsyncObservable<'a> -> IAsyncObservable<'a> Returns the values from the source observable sequence until the other observable sequence produces a value. val choose: ('a -> 'b option) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Applies the given function to each element of the stream and returns the stream comprised of the results for each element where the function returns Some with some value. val chooseAsync: ('a -> Async<'b option>) -> IAsyncObservable<'a> -> IAsyncObservable<'b> Applies the given async function to each element of the stream and returns the stream comprised of the results for each element where the function returns Some with some value.

2.3.4 Aggregating val scan: initial:'s -> accumulator:('s -> 'a -> 's) -> source: IAsyncObservable<'a> -> IAsyncObservable<'s> Applies an accumulator function over an observable sequence for every value ‘a and returns each intermedi- ate result ‘s. The initial seed value is used as the initial accumulator value. Returns an observable sequence containing the accumulated values ‘s. Example:

let scanner a x=a+x

let xs= AsyncRx.ofSeq<| seq { 1..5}|> AsyncRx.scan0 scanner val scanAsync: initial: 's -> accumulator: ('s -> 'a -> Async<'s>) -> source: IAsyncObservable<'a> -> IAsyncObservable<'s> Applies an async accumulator function over an observable sequence and returns each intermediate result. The seed value is used as the initial accumulator value. Returns an observable sequence containing the accumulated values. Example:

let scannerAsync a x= async { return a+x}

let xs= AsyncRx.ofSeq<| seq { 1..5}|> AsyncRx.scanAsync0 scannerAsync val groupBy: keyMapper: ('a -> 'g) -> source: IAsyncObservable<'a> -> IAsyncObservable> Groups the elements of an observable sequence according to a specified key mapper function. Returns a se- quence of observable groups, each of which corresponds to a given key. Example:

let xs= AsyncRx.ofSeq [1;2;3;4;5;6] |> AsyncRx.groupBy (fun x->x%2) |> AsyncRx.flatMap (fun x-> x)

2.3.5 Combining

Functions for combining multiple async observables into one.

14 Chapter 2. AsyncRx Fable.Reaction, Release 2.1.0

val merge: IAsyncObservable<'a> -> IAsyncObservable<'a> -> IAsyncObservable<'a> Merges an observable sequence with another observable sequence. val mergeInner: IAsyncObservable\\> -> IAsyncObservable<'a> Merges an observable sequence of observable sequences into an observable sequence. val switchLatest: IAsyncObservable> -> IAsyncObservable<'a> Transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence. val concat: seq> -> IAsyncObservable<'a> Concatenates an observable sequence with another observable sequence. val startWith: seq<'a> -> IAsyncObservable<'a> -> IAsyncObservable<'a> Prepends a sequence of values to an observable sequence. Returns the source sequence prepended with the specified values. val combineLatest: IAsyncObservable<'b> -> IAsyncObservable<'a> -> IAsyncObservable<'a*'b> Merges the specified observable sequences into one observable sequence by combining elements of the sources into tuples. Returns an observable sequence containing the combined results. val withLatestFrom: IAsyncObservable<'b> -> IAsyncObservable<'a> -> IAsyncObservable<'a*'b> Merges the specified observable sequences into one observable sequence by combining the values into tuples only when the first observable sequence produces an element. Returns the combined observable sequence. val zipSeq: seq<'b> -> IAsyncObservable<'a> -> IAsyncObservable<'a*'b> Zip given sequence with source. Combines one and one item from each stream into one tuple.

2.3.6 Time-shifting

Functions for time-shifting (IAsyncObservable<'a> -> IAsyncObservable<'a>) an async observable. val delay: int -> IAsyncObservable<'a> -> IAsyncObservable<'a> Time shifts the observable sequence by the given timeout. The relative time intervals between the values are preserved. val debounce: int -> IAsyncObservable<'a> -> IAsyncObservable<'a> Ignores values from an observable sequence which are followed by another value before the given timeout. val sample: msecs: int -> source: IAsyncObservable<'a> -> IAsyncObservable<'a> Samples the observable sequence at each interval.

2.3.7 Leaving

Functions for leaving (IAsyncObservable<'a> -> 'a) the async observable. val toAsyncSeq: IAsyncObservable<'a> -> AsyncSeq<'a> (Not available in Fable)

2.4 Types

FSharp.Control.AsyncRx is built around the interfaces IAsyncDisposable, IAsyncObserver and IAsyncObservable. This enables a familiar Rx programming syntax similar to FSharp.Control.Reactive with the difference that all methods are Async.

2.4. Types 15 Fable.Reaction, Release 2.1.0

type IAsyncDisposable= abstract member DisposeAsync: unit-> Async type IAsyncObserver<'a> = abstract member OnNextAsync:'a -> Async abstract member OnErrorAsync: exn-> Async abstract member OnCompletedAsync: unit-> Async type IAsyncObservable<'a> = abstract member SubscribeAsync: IAsyncObserver<'a> -> Async

The relationship between these three interfaces can be seen in this single line of code. You get an async disposable by subscribing asynchronously to an async observable with an async observer: async { let! disposableAsync = observable.SubscribeAsync observerAsync ... }

2.4.1 Async Observables

AsyncRx is an implementation of Async Observable. The difference between an “Async Observable” and an “Ob- servable” is that with “Async Observables” you need to await methods such as Subscribe, OnNext, OnError, and OnCompleted. In AsyncRx they are thus called SubscribeAsync, OnNextAsync, OnErrorAsync, and OnCompletedAsync. This enables SubscribeAsync to await async operations i.e setup network connections, and observers (OnNext) may finally await side effects such as writing to disk (observers are all about side-effects right?). This diagram shows the how Async Observables relates to other collections and values.

Single Value Multiple Values Synchronous pull unit -> ‘a seq<’a> Synchronous push ‘a -> unit Observable<’a> Asynchronous pull unit -> Async<’a> AsyncSeq<’a> Asynchronous push ‘a -> Async AsyncObservable<’a>

2.4.2 Async Observers

Observers (IAsyncObserver<'a>) may be subscribed to observables in order to receive notifications. An observer is defined as an implementation of IAsyncObserver<'a>: let _obv= { new IAsyncObserver<'a> with member this.OnNextAsync x= async { printfn"OnNext: %d"x } member this.OnErrorAsync err= async { printfn"OnError: %s" (ex.ToString()) } member this.OnCompletedAsync ()= async { printfn"OnCompleted" } }

16 Chapter 2. AsyncRx Fable.Reaction, Release 2.1.0

There is also a SubscribeAsync overload that takes a notification function so instead of using IAsyncObserver<'a> instances you may subscribe with a single async function taking a Notification<'a>.

let obv n= async { match n with | OnNext x-> printfn"OnNext: %d"x | OnError ex-> printfn"OnError: %s" (ex.ToString()) | OnCompleted-> printfn"OnCompleted" }

2.4.3 Notifications

Observers may written as a function accepting Notification<'a> which is defined as the following discriminated union:

type Notification<'a> = | OnNext of'a | OnError of exn | OnCompleted

2.5 Disposables

Asynchronous disposables are similar to IDisposable but with the difference that it encapsulates an async dispose method called DisposeAsync.

type AsyncDisposable (cancel) = interface IAsyncDisposable with member this.DisposeAsync () = async { do! cancel () }

In additon the following static members have been defined: • Create : (unit -> Async) -> IAsyncDisposable, creates an IAsyncDisposable from an async cancel function. Same instantiating the object, but the return value here is anonymous. • Empty : unit -> IAsyncDisposable, creates an empty disposable that will do nothing if it’s disposed. • Composite : seq -> IAsyncDisposable, creates a disposable from a se- quence of disposables. If disposed then all the contained disposables will also be disposed. The SubscribeAsync method for async observables returns an async disposable, and to cancel the subscription you need to call the DisposeAsync method.

let xs = ofSeq <| seq { 1 .. 5 } let obv = MyObserver() let! subscription = xs.SubscribeAsync obv

// Unsubscribe do! subscription.DisposeAsync ()

2.5. Disposables 17 Fable.Reaction, Release 2.1.0

2.6 Query Builder

Queries may be written by composing functions or using query expressions. let xs= asyncRx { yield 42 }

This expression is equivalent to: let xs= AsyncRx.single 42

You can also yield multiple values: let xs= asyncRx { yield 42 yield 43 }

This is equivalent to: let xs= AsyncRx.ofSeq [42; 43]

// or let xs= AsyncRx.concat [AsyncRx.single 42; AsyncRx.single 43]

2.6.1 Flat mapping let xs = asyncRx { let! i = AsyncRx.single 42 yield i*10 }

This is equivalent to: let xs= AsyncRx.single 42 |> AsyncRx.flatMap (fun i-> AsyncRx.single (i * 10)) }

2.6.2 More advanced example

These two examples below are equivalent:

Seq.toList"TIME FLIES LIKE AN ARROW" |> Seq.mapi (fun i c-> i, c) |> AsyncRx.ofSeq |> AsyncRx.flatMap (fun (i, c)-> AsyncRx.fromMouseMoves () |> AsyncRx.delay (100 * i) |> AsyncRx.map (fun m-> Letter (i, string c, intm.clientX, intm.clientY)))

18 Chapter 2. AsyncRx Fable.Reaction, Release 2.1.0

The above query may be written in query expression style: asyncRx { let! i, c = Seq.toList "TIME FLIES LIKE AN ARROW" |> Seq.mapi (fun i c -> i, c) |> AsyncRx.ofSeq let ms = AsyncRx.fromMouseMoves () |> delay (100 * i) for m in ms do yield Letter (i, string c, int m.clientX, int m.clientY) }

2.7 Subjects

You can think of a “Subject” as being a tube that is open at both sides. Whatever you put in on one side, pops out at the other end. Subjects combines both the IAsyncObservable<'a> and IAsyncObserver<'a> interfaces. Whatever you put in (e.g OnNextAsync) at one side (IAsyncObserver<'a>) will come out of the other end (IAsyncObservable<'a>). The subject in FSharp.AsyncRx is very similar to the classic Subject in ReactiveX. The difference is that a is not a single object, but a tuple of two entangled objects where one implements IAsyncObserver<'a>, and the other implements IAsyncObservable<'a>. This solves the problem of having a single object trying to be two things at once (what is the dual of an ISubject<'a> anyways?). There are currently 3 types of subjects in AsyncRx, subject, mbSubject and singleSubject. val subject: unit -> IAsyncObserver<'a> * IAsyncObservable<'a> The subject will forward any notification pushed to the IAsyncObserver<'a> side to all observers that have subscribed to the IAsyncObservable<'a> side. The subject is hot in the sense that if there are no observers, then the notification will be lost.

let dispatch, obs = AsyncRx.subject ()

let main = async { let! sub = obs.SubscribeAsync obv do! dispatch.OnNextAsync 42 ... }

Async.StartImmediate main () val mbSubject: unit -> MailboxProcessor> * IAsyncObservable<'a> The Mailbox Subject is the same as a subject except that the observer is exposed as a MailboxProcessor>. The mailbox subject is hot in the sense that if there are no observers, then any pushed notification will be lost.

let dispatch, obs = AsyncRx.mbSubject ()

let main = async { let! sub = obs.SubscribeAsync obv do dispatch.Post (OnNext 42) ... }

Async.StartImmediate main ()

2.7. Subjects 19 Fable.Reaction, Release 2.1.0 val singleSubject: unit -> IAsyncObserver<'a> * IAsyncObservable<'a> The singleSubject will forward any notification pushed to the IAsyncObserver<'a> side to a single ob- server that have subscribed to the IAsyncObservable<'a> side. The singleSubject is “cold” in the sense that if there’s no-one observing, then the writer will be awaited until there is a subscriber that can observe the value being pushed. You can use a singleSubject in scenarios that requires so called backpressure.

let dispatch, obs = AsyncRx.singleSubject ()

let main = async { let! sub = obs.SubscribeAsync obv do! dispatch.OnNextAsync 42 ... }

Async.StartImmediate main ()

2.8 Error Handling

Reactive applications also need to handle exceptions. But unlike normal applications where exceptions fly upwards, exceptions in a reactive application goes the other way and propagates down the chain of operators. Observables will dispose the subscription if any errors occur. No event can be observed after an OnError. This may sound like a restriction, but if we think of the dual world of iterables, then we know that if the throws an exception, then the iteration is over. Observables works in just the same but opposite way. When dealing with errors there are several strategies to choose: • Retrying subscriptions • Catching exceptions • Ignoring exceptions • Logging errors It’s important to notice that observer implementations should never throw exceptions. This is because we expect that (1) the observer implementor knows best how to handle those exceptions and we can’t do anything reasonable with them, and (2) if an exception occurs then we want that to bubble out of and not into the reactive framework. The following error handling operators have been implemented: val catch: (exn -> IAsyncObservable<'a>) -> IAsyncObservable<'a> -> IAsyncObservable<'a> Returns an observable sequence containing the first sequence’s elements, followed by the elements of the handler sequence in case an exception occurred.

2.9 Custom Observables

There are many ways to create custom observble streams. Note that the goal is to have some kind of create function that returns an IAsyncObservable<'a>. We will go through a few options for creating such a custom stream.

2.9.1 1. Use a Subject

A Subject in AsyncRx return both an observer (IAsyncObserver) and an observable (IAsyncObservable). Note that we need to use Async.Start to start the worker function so it runs concurrently.

20 Chapter 2. AsyncRx Fable.Reaction, Release 2.1.0

open FSharp.Control let myStream () = let dispatch, obs = AsyncRx.subject ()

let worker () = async { while true do let! msg = getMessageAsync () do! dispatch.OnNextAsync msg }

Async.Start (worker ()) obs

2.9.2 2. Use Create

The AsyncRx.Create function takes an Async subscribe function and returns an IAsyncObservable. Note that we need to use Async.Start to start the worker function so it runs concurrently. open FSahrp.Control let myStream () = let subscribeAsync (obs: IAsyncObserver) : Async = let mutable running = true

async { let worker () = async { while running do let! msg = getMessageAsync () do! obs.OnNextAsync msg }

Async.Start (worker ())

let cancel () = async { running <- false }

return AsyncDisposable.Create(cancel) }

AsyncRx.create(subscribeAsync)

2.9.3 3. Use ofAsyncWorker

The ofAsyncWorker is a handy utility function for creating an IAsyncObservable from an async worker function, where the worker function has the type IAsyncObserver<'a> -> CancellationToken -> Async. Thus the worker will receive a cancellation token that can be used to detect if cancellation (dis- pose) have been requested. open System.Threading open FSharp.Control let myStream' () = (continues on next page)

2.9. Custom Observables 21 Fable.Reaction, Release 2.1.0

(continued from previous page) let worker (obv: IAsyncObserver) (token: CancellationToken) = async { while not token.IsCancellationRequested do let! msg = getMessageAsync () do! obv.OnNextAsync msg }

AsyncRx.ofAsyncWorker(worker)

• search

22 Chapter 2. AsyncRx CHAPTER 3

Indices and tables

• genindex • search

23 Fable.Reaction, Release 2.1.0

24 Chapter 3. Indices and tables Index

C mbSubject (val), 19 catch (val), 13, 20 merge (val), 14 choose (val), 14 mergeInner (val), 15 chooseAsync (val), 14 msgChannel<’msg> (val),7 combineLatest (val), 15 concat (val), 15 O create (val), 11 ofAsyncSeq (val), 11 ofSeq (val), 11 D debounce (val), 15 S defer (val), 11 sample (val), 15 delay (val), 15 scan (val), 14 distinctUntilChanged (val), 14 scanAsync (val), 14 single (val), 10 E singleSubject (val), 19 empty (val), 10 startWith (val), 15 subject (val), 19 F switchLatest (val), 15 fail (val), 11 filter (val), 13 T filterAsync (val), 13 takeUntil (val), 14 flatMap (val), 13 timer (val), 11 flatMapAsync (val), 13 toAsyncSeq (val), 15 flatMapi (val), 13 flatMapiAsync (val), 13 W flatMapLatest (val), 13 withLatestFrom (val), 15 flatMapLatestAsync (val), 13 Z G zipSeq (val), 15 groupBy (val), 14 I interval (val), 11 M map (val), 11 mapAsync (val), 12 mapi (val), 12 mapiAsync (val), 12

25