hyper.dev

The new and improved ff.scm would look like:

(define (create-app container init view)
  (let ((state (init)))  ;; init state
    (letrec ((make-controller
              (lambda (action)
                ;; passed to view, used to wrap DOM event callback.
                (lambda args
                  (let ((new (apply (action state args))))
                    (set! state new)
                    (render!)))))
             (render!
              (lambda ()
                (set! container (diff+patch! container (view state make-controller)))))))
      (render!)))

That is only part of the story, because action can be paused to execute something asynchronous. Take a look at an improved createApp written in JavaScript for the browser:

let createSimpleApp = function(app, root, init, view) {
    let model = init();
    let render;

    let makeController = function(controller) {
        return function(event) {
            // XXX: This might be performance bottleneck
            // https://fb.me/react-event-pooling
            event.persist()
            let promise = controller(app, model, event);
            promise.then(function(transformer) {
                // XXX: if the controller returns nothing
                // this will lead to an 'undefined' error
                // which is not very friendly.
                let newModel = transformer();
                model = newModel;  // XXX: side effect
                render();
            });
        }
    };

    /* Render the application */
    render = function() {
        pk('rendering');
        let html = view(model, makeController);
        ReactDOM.render(html, root);
    };

    // sneak into an application from the outside.
    return function(change) {
        let promise = change(app, model);
        promise.then(function(transformer) {
            if(transformer) {
                let newModel = transformer();
                model = newModel;  // XXX: side effect
                render();
            }
        });
    };
};