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.
Permalink
Failed to load latest commit information.
Examples/Counter
Mu
Tests
.gitignore
.travis.yml
LICENSE
Mu.sln
README.md

README.md

Mu

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
    Mu.run 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

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

Update

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.

View

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.

Run

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:

Mu.run' component
// or
Mu.run init update view

Examples

👉 Examples contain some advance usages of async effects.

Usage

LICENSE

MIT

Author