Apply The Elm Architecture pattern on Xamarin stack, with another simple, unobtrusive way
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Apply The Elm Architecture pattern on Xamarin stack, with another simple, unobtrusive way.

Build Status

The Elm Architecture (TEA)

TEA is all about Model, Update, and View, in Mu, it's specific to:

  • Model — the state of a GUI component
  • Update — a way to update your state
  • View — a way to view your state as UIView(Controller), NSView(Controller), Activity and other platform view stacks that Xamarin supported

This is a simple pattern for architecting GUI components.

// Model
type Model = { ... }

// Actions, all update through action to perform
type Action = Reset | ...

// Update
let update model action =
  match action with
  | Reset -> Update { model with ... }

// View
type ViewController (handle:IntPtr) =
  inherit UIViewController (handle)

  override x.ViewDidLoad () =
    base.ViewDidLoad ()
    // Glue model update view initModel update x

  interface Mu.IView<Model, Action> with
    member x.BindModel model binder =
      binder.Bind <@ model.Field @> (fun fieldValue -> x.someLabel.Text <- fieldValue)

    member x.BindAction send =
      x.resetButton.TouchUpInside.Add (fun _ -> send Reset)

That is really the essence of The Elm Architecture!

With Mu, Model and Update are separated from view, this means that they can shared across platforms, implementing Mu.IView<'model, 'acition> interfaces is the only required step to support specific platform.


Model should be a DTO(Data Transfer Object) only, immutable record is the best way to represent model in F#.


type Update<'model, 'action> =
  | NoUpdate
  | Update of 'model
  | UpdateWithEffects of 'model * Effects<'model, 'action>
  | Effects of Effects<'model, 'action>
and Effects<'model, 'action> =
  | Eff of ('model -> unit)
  | Cmd of ('model -> 'action)
  | AsyncCmd of ('model -> Async<'action>)
  | AsyncCmd' of ('model -> Async<'action> * CancellationTokenSource)

Update is about changing model through action: 'model -> 'action -> Update<'model, 'action>.

Not all updates are just model changes, side effects without or with model changing are common. Mu provides current model state and the action sender when performing side effects.


type IView<'model, 'action> =
  abstract BindModel: 'model -> IBinder<'action> -> unit
  abstract BindAction: Action<'action> -> unit
and IBinder<'action> =
  abstract Bind: Expr<'value> -> ('value -> unit) -> unit
  abstract Send: Action<'action>
and Action<'action> =
  'action -> unit

View is only an interface in Mu, this is the most unobtrusive way to introduce 3rd lib into your project. BindModel provides model and binder to sync model states to view elements, and BindAction provides an action sender to make user input possible.


A Mu component is simply a record contained model initialization, model updater and view:

type T<'model, 'action> =
  { init: unit -> 'model
    update: 'model -> 'action -> Update<'model, 'action>
    view: IView<'model, 'action> }

Run component on view when it's ready with:' component
// or init update view


👉 Examples contain some advance usages of async effects.