Structuring FRP (specifically Note) applications

I think that your state should include a status telling if you are Loading or Running the application :

type t =
  { status : [`Loading | `Running ]
  ; data : …
  }

As long as you do not store functionnal values inside the state, you can add as many elements as you want (if you want to add such elements in the state, you have to provide your own compare function in order to avoid error in runtime).

Then, here is how I run my event loop: each event is handled inside it’s own module and I avoid the monolithic pattern matching:

(** The Make module build the main application loop.

    The function [run] update the state on each event, and return a new state.
    Each event must follow the [event] type, which is composed from the type
    [t], and a module with a fonction [update].

    This example create an application with the state containing a simple
    counter. An even which increment this counter is created and can be used to
    update the state.


    [
        type state = { value : int }

        (** Increment the state. *)
        module Incr = struct
            type t = unit

            let update () state = { value = state.value + 1 }
        end

        (** Decrement the state. *)
        module Incr = struct
            type t = unit

            let update () state = { value = state.value - 1 }
        end

        module App = Make(struct type t = state end)

        (* Create the events *)
        let incr_event = App.E ((), (module Incr:App.Event with type t = Incr.t))
        let decr_event = App.E ((), (module Decr:App.Event with type t = Decr.t))

        let init = { value = 0 } in

        (* Run the main loop *)
        let state = App.run
          init
          (E.select
            [ incr_event
            ; decr_event ] ) in …
    ]

*)
module Make(S:sig type t end) = struct
  module type Event = sig

    type t

    val update: t -> S.t -> S.t

  end

  type event = E : 'a * (module Event with type t = 'a) -> event

  (** Simple helper for the main event loop *)
  let run
    : ?eq:(S.t -> S.t -> bool) -> S.t -> event Note.E.t -> S.t Note.S.t
    = fun ?eq init event ->
      let action = Note.E.map (fun (E (t, (module Event))) st -> Event.update t st) event in
      Note.S.accum ?eq init action
end

I have applied this pattern in differents applications, and this works fine. I’ve found the inspiration in this post : The shape design problem - #26 by kantian

Hope this helps :slight_smile:

1 Like