F# Implementation of The Elm Architecture - Part 2

This is the second part of a prototype of The Elm Architecture in F#. The first post covered the logical UI and this covers using it with WPF and Xamarin.

Native UI Implementation

Below is the WPF implementation of the INativeUI interface. The Xamarin implementation is very similar.

Threading proves to be simple with all the updates being performed on the UI thread in a single call. UIUpdate maps well to the operations required to locate and update the native UI elements.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
65: 
module WPF =
    let CreateNaiveUI (root:ContentControl) =
        
        let rec createUI ui : UIElement =
            match ui with
            |Text text ->
                let c = Label(Content=string text)
                upcast c
            |Input (text,event) ->
                let c = TextBox(Text=string text)
                let event = !event
                c.TextChanged.Add(fun _ -> let t = c.Text
                                           async { !event t } |> Async.Start)
                upcast c
            |Button (text,event) ->
                let c = Button(Content=string text)
                let event = !event
                c.Click.Add(fun _ -> async { (!event)() } |> Async.Start)
                upcast c
            |Div (layout,list) ->
                let children = List.map createUI list
                let c = StackPanel(Orientation=
                                    match layout with 
                                    |Vertical->Orientation.Vertical
                                    |Horizontal->Orientation.Horizontal)
                List.iter (c.Children.Add>>ignore) children
                upcast c

        let rec locatePanel loc : Panel =
            match loc with
            |[] -> root.Content :?> _
            |i::xs -> (locatePanel xs).Children.Item i :?> _

        let uiUpdate u =
            match u with
            | InsertUI (loc,ui) ->
                match loc with
                |[] -> root.Content <- createUI ui
                |i::xs -> (locatePanel xs).Children.Insert(i,createUI ui)
            | UpdateUI (loc,ui) ->
                let element = match loc with
                              |[] -> root.Content :?> _
                              |i::xs -> (locatePanel xs).Children.Item i
                match ui with
                | Text text -> (element :?> Label).Content <- string text
                | Input (text,_) -> (element :?> TextBox).Text <- string text
                | Button (text,_) -> (element :?> Button).Content <- string text
                | Div _ -> ()
            | ReplaceUI (loc,ui) ->
                match loc with
                |[] -> root.Content <- createUI ui
                |i::xs ->
                    let c = (locatePanel xs).Children
                    c.RemoveAt i
                    c.Insert(i,createUI ui)
            | RemoveUI loc ->
                match loc with
                |[] -> ()
                |i::xs -> (locatePanel xs).Children.RemoveAt i
            | EventUI _ -> ()

        { new INativeUI with
            member __.Send list =
                root.Dispatcher.Invoke (fun () -> List.iter uiUpdate list)
        }

Results

The same CounterList UI application from the previous post has been used across a number of native UIs. The example source produces the following mobile and desktop UIs.

Conclusion

The Elm Architecture continues to look to be a very promising pattern.

The INativeUI implementation is a single place for native UI element creation and is much more DRY than other UI models. So far, styling has not been considered, but this single place should make it easier with both an Elm and a CSS model being possible.

The Elm Architecture moves the view and event logic away from the native UI. This has the benefit of making the UI more testable and at the same time making migration of the native UI easier.

These benefits, combined with the type safety and composability outlined in the previous post, make this pattern compelling.

UPDATED (Aw shucks):

Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val remove : n:int -> l:'a list -> 'a list * 'a list

Full name: Main.List.remove
val n : int
val l : 'a list
val pop : (int -> 'b list -> 'b list -> 'b list * 'b list)
val l : 'b list
val p : 'b list
val x : 'b
val xs : 'b list
val add : p:'a list -> l:'a list -> 'a list

Full name: Main.List.add
val p : 'a list
val x : 'a
val xs : 'a list
val removei : i:int -> l:'a list -> 'a list

Full name: Main.List.removei
val i : int
val t : 'a list
val tail : list:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.tail
val replacei : i:int -> item:'a -> l:'a list -> 'a list

Full name: Main.List.replacei
val item : 'a
val revMap : mapping:('a -> 'b) -> list:'a list -> 'b list

Full name: Main.List.revMap
val mapping : ('a -> 'b)
Multiple items
val list : 'a list

--------------------
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val work : ('a list -> 'b list -> 'b list)
val input : 'a list
val output : 'b list
val mapAt : i:int -> mapping:('a -> 'a) -> list:'a list -> 'a list

Full name: Main.List.mapAt
val mapping : ('a -> 'a)
val removed : 'a list
val tail : 'a list
val head : list:'T list -> 'T

Full name: Microsoft.FSharp.Collections.List.head
Multiple items
module Event

from Microsoft.FSharp.Control

--------------------
type 'msg Event = ('msg -> unit) ref ref

Full name: Main.Event<_>


 Message event used on the primative UI components.


--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
  new : unit -> Event<'Delegate,'Args>
  member Trigger : sender:obj * args:'Args -> unit
  member Publish : IEvent<'Delegate,'Args>

Full name: Microsoft.FSharp.Control.Event<_,_>

--------------------
new : unit -> Event<'Delegate,'Args>
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
Multiple items
val ref : value:'T -> 'T ref

Full name: Microsoft.FSharp.Core.Operators.ref

--------------------
type 'T ref = Ref<'T>

Full name: Microsoft.FSharp.Core.ref<_>
type Layout =
  | Horizontal
  | Vertical

Full name: Main.Layout


 Layout for a section of UI components.
union case Layout.Horizontal: Layout
union case Layout.Vertical: Layout
type UI =
  | Text of string
  | Input of string * string Event
  | Button of string * unit Event
  | Div of Layout * UI list

Full name: Main.UI


 Primative UI components.
union case UI.Text: string -> UI
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
union case UI.Input: string * string Event -> UI
union case UI.Button: string * unit Event -> UI
union case UI.Div: Layout * UI list -> UI
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
type UIUpdate =
  | InsertUI of int list * UI
  | UpdateUI of int list * UI
  | ReplaceUI of int list * UI
  | RemoveUI of int list
  | EventUI of (unit -> unit)

Full name: Main.UIUpdate


 UI component update and event redirection.
union case UIUpdate.InsertUI: int list * UI -> UIUpdate
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
union case UIUpdate.UpdateUI: int list * UI -> UIUpdate
union case UIUpdate.ReplaceUI: int list * UI -> UIUpdate
union case UIUpdate.RemoveUI: int list -> UIUpdate
union case UIUpdate.EventUI: (unit -> unit) -> UIUpdate
Multiple items
type UI =
  | Text of string
  | Input of string * string Event
  | Button of string * unit Event
  | Div of Layout * UI list

Full name: Main.UI


 Primative UI components.


--------------------
type 'msg UI =
  {UI: UI;
   mutable Event: 'msg -> unit;}

Full name: Main.UI<_>


 UI component including a message event.
Multiple items
UI.UI: UI

--------------------
type UI =
  | Text of string
  | Input of string * string Event
  | Button of string * unit Event
  | Div of Layout * UI list

Full name: Main.UI


 Primative UI components.


--------------------
type 'msg UI =
  {UI: UI;
   mutable Event: 'msg -> unit;}

Full name: Main.UI<_>


 UI component including a message event.
Multiple items
UI.Event: 'msg -> unit

--------------------
module Event

from Microsoft.FSharp.Control

--------------------
type 'msg Event = ('msg -> unit) ref ref

Full name: Main.Event<_>


 Message event used on the primative UI components.


--------------------
type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =
  new : unit -> Event<'Delegate,'Args>
  member Trigger : sender:obj * args:'Args -> unit
  member Publish : IEvent<'Delegate,'Args>

Full name: Microsoft.FSharp.Control.Event<_,_>

--------------------
new : unit -> Event<'Delegate,'Args>
type App<'msg,'model> =
  {Model: 'model;
   Update: 'msg -> 'model -> 'model;
   View: 'model -> 'msg UI;}

Full name: Main.App<_,_>


 UI application.
App.Model: 'model
App.Update: 'msg -> 'model -> 'model
App.View: 'model -> 'msg UI
type INativeUI =
  interface
    abstract member Send : UIUpdate list -> unit
  end

Full name: Main.INativeUI


 Native UI interface.
abstract member INativeUI.Send : UIUpdate list -> unit

Full name: Main.INativeUI.Send
Multiple items
type CompilationRepresentationAttribute =
  inherit Attribute
  new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
  member Flags : CompilationRepresentationFlags

Full name: Microsoft.FSharp.Core.CompilationRepresentationAttribute

--------------------
new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute
type CompilationRepresentationFlags =
  | None = 0
  | Static = 1
  | Instance = 2
  | ModuleSuffix = 4
  | UseNullAsTrueValue = 8
  | Event = 16

Full name: Microsoft.FSharp.Core.CompilationRepresentationFlags
CompilationRepresentationFlags.ModuleSuffix: CompilationRepresentationFlags = 4
val memoize<'model,'msg (requires reference type and reference type)> : (('model -> 'msg UI) -> 'model -> 'msg UI) (requires reference type and reference type)

Full name: Main.UIModule.memoize


 Memoize view generation from model object references.
val not : value:bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
val d : System.Runtime.CompilerServices.ConditionalWeakTable<'model,'msg UI> (requires reference type and reference type)
namespace System
namespace System.Runtime
namespace System.Runtime.CompilerServices
Multiple items
type ConditionalWeakTable<'TKey,'TValue (requires reference type and reference type)> =
  new : unit -> ConditionalWeakTable<'TKey, 'TValue>
  member Add : key:'TKey * value:'TValue -> unit
  member GetOrCreateValue : key:'TKey -> 'TValue
  member GetValue : key:'TKey * createValueCallback:CreateValueCallback<'TKey, 'TValue> -> 'TValue
  member Remove : key:'TKey -> bool
  member TryGetValue : key:'TKey * value:'TValue -> bool
  nested type CreateValueCallback

Full name: System.Runtime.CompilerServices.ConditionalWeakTable<_,_>

--------------------
System.Runtime.CompilerServices.ConditionalWeakTable() : unit
val view : ('model -> 'msg UI) (requires reference type and reference type)
val model : 'model (requires reference type)
System.Runtime.CompilerServices.ConditionalWeakTable.TryGetValue(key: 'model, value: byref<'msg UI>) : bool
val ui : 'msg UI (requires reference type)
System.Runtime.CompilerServices.ConditionalWeakTable.Add(key: 'model, value: 'msg UI) : unit
val text : text:string -> 'a UI

Full name: Main.UIModule.text


 Returns a Text display UI component.
val text : string
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val input : text:string -> string UI

Full name: Main.UIModule.input


 Returns a text Input UI component.
val ev : (string -> unit) ref ref
val ui : string UI
val raise : (string -> unit)
val a : string
UI.Event: string -> unit
val button : text:string -> msg:'a -> 'a UI

Full name: Main.UIModule.button


 Returns a Button UI component.
val msg : 'a
val ev : (unit -> unit) ref ref
val ui : 'a UI
UI.Event: 'a -> unit
val div : layout:Layout -> list:'a UI list -> 'a UI

Full name: Main.UIModule.div


 Returns a section of UI components given a layout.
 The name div comes from HTML and represents a division (or section) of UI components.
val layout : Layout
Multiple items
val list : 'a UI list

--------------------
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
Multiple items
module List

from Main

--------------------
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val map : mapping:('T -> 'U) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.map
UI.UI: UI
val raise : ('a -> unit)
val a : 'a
val iter : action:('T -> unit) -> list:'T list -> unit

Full name: Microsoft.FSharp.Collections.List.iter
val i : 'a UI
val map : f:('a -> 'b) -> ui:'a UI -> 'b UI

Full name: Main.UIModule.map


 Returns a new UI component mapping the message event using the given function.
val f : ('a -> 'b)
val ui2 : 'b UI
val e : 'a
UI.Event: 'b -> unit
val diff : ui1:'a UI -> ui2:'b UI -> UIUpdate list

Full name: Main.UIModule.diff


 Returns a list of UI updates from two UI components.
val ui1 : 'a UI
val update : ('c ref ref -> 'c ref ref -> unit -> unit)
val e1 : 'c ref ref
val e2 : 'c ref ref
val ev : 'c ref
val diff : (UI -> UI -> int list -> int -> UIUpdate list -> UIUpdate list)
val ui1 : UI
val ui2 : UI
val path : int list
val index : int
val diffs : UIUpdate list
module LanguagePrimitives

from Microsoft.FSharp.Core
val PhysicalEquality : e1:'T -> e2:'T -> bool (requires reference type)

Full name: Microsoft.FSharp.Core.LanguagePrimitives.PhysicalEquality
val t1 : string
val t2 : string
val e1 : unit Event
val e2 : unit Event
val e1 : string Event
val e2 : string Event
val l1 : Layout
val l2 : Layout
val l : UI list
val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State

Full name: Microsoft.FSharp.Collections.List.fold
val ui : UI
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val l : Layout
val h1 : UI
val t1 : UI list
val h2 : UI
val t2 : UI list
val h3 : UI
val app : model:'a -> update:('b -> 'a -> 'a) -> view:('a -> 'b UI) -> App<'b,'a>

Full name: Main.UIModule.app


 Returns a UI application from a UI model, update and view.
val model : 'a
val update : ('b -> 'a -> 'a)
val view : ('a -> 'b UI)
val run : nativeUI:INativeUI -> app:App<'a,'b> -> unit

Full name: Main.UIModule.run


 Runs a UI application given a native UI.
val nativeUI : INativeUI
val app : App<'a,'b>
val handle : ('b -> 'a UI -> 'a -> unit)
val model : 'b
val newModel : 'b
App.Update: 'a -> 'b -> 'b
val newUI : 'a UI
App.View: 'b -> 'a UI
val diff : UIUpdate list
val f : (unit -> unit)
abstract member INativeUI.Send : UIUpdate list -> unit
App.Model: 'b
type Msg =
  | Increment
  | Decrement

Full name: Main.Counter.Msg
union case Msg.Increment: Msg
union case Msg.Decrement: Msg
type Model = int

Full name: Main.Counter.Model
val init : i:Model -> Model

Full name: Main.Counter.init
val i : Model
val update : msg:Msg -> model:int -> Model

Full name: Main.Counter.update
val msg : Msg
val model : int
val view : model:Model -> Msg UI

Full name: Main.Counter.view
val model : Model
Multiple items
module UI

from Main

--------------------
type UI =
  | Text of string
  | Input of string * string Event
  | Button of string * unit Event
  | Div of Layout * UI list

Full name: Main.UI


 Primative UI components.


--------------------
type 'msg UI =
  {UI: UI;
   mutable Event: 'msg -> unit;}

Full name: Main.UI<_>


 UI component including a message event.
val app : i:Model -> App<Msg,Model>

Full name: Main.Counter.app
type Msg =
  | Reset
  | Top of Msg
  | Bottom of Msg

Full name: Main.CounterPair.Msg
union case Msg.Reset: Msg
union case Msg.Top: Counter.Msg -> Msg
module Counter

from Main
union case Msg.Bottom: Counter.Msg -> Msg
type Model =
  {Top: Model;
   Bottom: Model;}

Full name: Main.CounterPair.Model
Model.Top: Counter.Model
Model.Bottom: Counter.Model
val init : top:Counter.Model -> bottom:Counter.Model -> Model

Full name: Main.CounterPair.init
val top : Counter.Model
val bottom : Counter.Model
val init : i:Counter.Model -> Counter.Model

Full name: Main.Counter.init
val update : msg:Msg -> model:Model -> Model

Full name: Main.CounterPair.update
val msg : Counter.Msg
val update : msg:Counter.Msg -> model:int -> Counter.Model

Full name: Main.Counter.update
val view : model:Model -> Msg UI

Full name: Main.CounterPair.view
val view : model:Counter.Model -> Counter.Msg UI

Full name: Main.Counter.view
val app : top:Counter.Model -> bottom:Counter.Model -> App<Msg,Model>

Full name: Main.CounterPair.app
type Msg =
  | Insert
  | Remove
  | Modify of int * Msg

Full name: Main.CounterList.Msg
union case Msg.Insert: Msg
union case Msg.Remove: Msg
union case Msg.Modify: int * Counter.Msg -> Msg
type Model =
  {Counters: Model list;}

Full name: Main.CounterList.Model
Model.Counters: Counter.Model list
val init : Model

Full name: Main.CounterList.init
val update : msg:Msg -> model:Model -> Model

Full name: Main.CounterList.update
val xs : Counter.Model list
val view : model:Model -> Msg UI

Full name: Main.CounterList.view
val mapi : mapping:(int -> 'T -> 'U) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.mapi
val c : Counter.Model
val v : Counter.Msg
val app : App<Msg,Model>

Full name: Main.CounterList.app
namespace System.Windows
namespace System.Windows.Controls
module WPF

from Main
val CreateNaiveUI : root:ContentControl -> INativeUI

Full name: Main.WPF.CreateNaiveUI
val root : ContentControl
Multiple items
type ContentControl =
  inherit Control
  new : unit -> ContentControl
  member Content : obj with get, set
  member ContentStringFormat : string with get, set
  member ContentTemplate : DataTemplate with get, set
  member ContentTemplateSelector : DataTemplateSelector with get, set
  member HasContent : bool
  member ShouldSerializeContent : unit -> bool
  static val ContentProperty : DependencyProperty
  static val HasContentProperty : DependencyProperty
  static val ContentTemplateProperty : DependencyProperty
  ...

Full name: System.Windows.Controls.ContentControl

--------------------
ContentControl() : unit
val createUI : (UI -> UIElement)
Multiple items
type UIElement =
  inherit Visual
  new : unit -> UIElement
  member AddHandler : routedEvent:RoutedEvent * handler:Delegate -> unit + 1 overload
  member AddToEventRoute : route:EventRoute * e:RoutedEventArgs -> unit
  member AllowDrop : bool with get, set
  member ApplyAnimationClock : dp:DependencyProperty * clock:AnimationClock -> unit + 1 overload
  member AreAnyTouchesCaptured : bool
  member AreAnyTouchesCapturedWithin : bool
  member AreAnyTouchesDirectlyOver : bool
  member AreAnyTouchesOver : bool
  member Arrange : finalRect:Rect -> unit
  ...

Full name: System.Windows.UIElement

--------------------
UIElement() : unit
val c : Label
Multiple items
type Label =
  inherit ContentControl
  new : unit -> Label
  member Target : UIElement with get, set
  static val TargetProperty : DependencyProperty

Full name: System.Windows.Controls.Label

--------------------
Label() : unit
Multiple items
union case UI.Input: string * string Event -> UI

--------------------
namespace System.Windows.Input
val event : string Event
val c : TextBox
Multiple items
type TextBox =
  inherit TextBoxBase
  new : unit -> TextBox
  member CaretIndex : int with get, set
  member CharacterCasing : CharacterCasing with get, set
  member Clear : unit -> unit
  member GetCharacterIndexFromLineIndex : lineIndex:int -> int
  member GetCharacterIndexFromPoint : point:Point * snapToText:bool -> int
  member GetFirstVisibleLineIndex : unit -> int
  member GetLastVisibleLineIndex : unit -> int
  member GetLineIndexFromCharacterIndex : charIndex:int -> int
  member GetLineLength : lineIndex:int -> int
  ...

Full name: System.Windows.Controls.TextBox

--------------------
TextBox() : unit
val event : (string -> unit) ref
event Primitives.TextBoxBase.TextChanged: IEvent<TextChangedEventHandler,TextChangedEventArgs>
member System.IObservable.Add : callback:('T -> unit) -> unit
val t : string
property TextBox.Text: string
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task -> Async<unit>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.Start : computation:Async<unit> * ?cancellationToken:System.Threading.CancellationToken -> unit
Multiple items
type Button =
  inherit ButtonBase
  new : unit -> Button
  member IsCancel : bool with get, set
  member IsDefault : bool with get, set
  member IsDefaulted : bool
  static val IsDefaultProperty : DependencyProperty
  static val IsCancelProperty : DependencyProperty
  static val IsDefaultedProperty : DependencyProperty

Full name: System.Windows.Controls.Button

--------------------
Button() : unit
val event : unit Event
val c : Button
val event : (unit -> unit) ref
event Primitives.ButtonBase.Click: IEvent<RoutedEventHandler,RoutedEventArgs>
Multiple items
val list : UI list

--------------------
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val children : UIElement list
val c : StackPanel
Multiple items
type StackPanel =
  inherit Panel
  new : unit -> StackPanel
  member CanHorizontallyScroll : bool with get, set
  member CanVerticallyScroll : bool with get, set
  member ExtentHeight : float
  member ExtentWidth : float
  member HorizontalOffset : float
  member LineDown : unit -> unit
  member LineLeft : unit -> unit
  member LineRight : unit -> unit
  member LineUp : unit -> unit
  ...

Full name: System.Windows.Controls.StackPanel

--------------------
StackPanel() : unit
type Orientation =
  | Horizontal = 0
  | Vertical = 1

Full name: System.Windows.Controls.Orientation
field Orientation.Vertical = 1
field Orientation.Horizontal = 0
property Panel.Children: UIElementCollection
UIElementCollection.Add(element: UIElement) : int
val locatePanel : (int list -> Panel)
val loc : int list
type Panel =
  inherit FrameworkElement
  member Background : Brush with get, set
  member Children : UIElementCollection
  member IsItemsHost : bool with get, set
  member ShouldSerializeChildren : unit -> bool
  static val BackgroundProperty : DependencyProperty
  static val IsItemsHostProperty : DependencyProperty
  static val ZIndexProperty : DependencyProperty
  static member GetZIndex : element:UIElement -> int
  static member SetZIndex : element:UIElement * value:int -> unit

Full name: System.Windows.Controls.Panel
property ContentControl.Content: obj
val xs : int list
val uiUpdate : (UIUpdate -> unit)
val u : UIUpdate
val element : UIElement
val c : UIElementCollection
UIElementCollection.RemoveAt(index: int) : unit
UIElementCollection.Insert(index: int, element: UIElement) : unit
Multiple items
val list : UIUpdate list

--------------------
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
property Threading.DispatcherObject.Dispatcher: Threading.Dispatcher
Threading.Dispatcher.Invoke(method: System.Delegate, [<System.ParamArray>] args: obj []) : obj
Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, method: System.Delegate) : obj
Threading.Dispatcher.Invoke(method: System.Delegate, timeout: System.TimeSpan, [<System.ParamArray>] args: obj []) : obj
Threading.Dispatcher.Invoke(method: System.Delegate, priority: Threading.DispatcherPriority, [<System.ParamArray>] args: obj []) : obj
Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, timeout: System.TimeSpan, method: System.Delegate) : obj
Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, method: System.Delegate, arg: obj) : obj
Threading.Dispatcher.Invoke(method: System.Delegate, timeout: System.TimeSpan, priority: Threading.DispatcherPriority, [<System.ParamArray>] args: obj []) : obj
Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, timeout: System.TimeSpan, method: System.Delegate, arg: obj) : obj
Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, method: System.Delegate, arg: obj, [<System.ParamArray>] args: obj []) : obj
Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, timeout: System.TimeSpan, method: System.Delegate, arg: obj, [<System.ParamArray>] args: obj []) : obj