Skip to content

Instantly share code, notes, and snippets.

@ericgj
Last active October 28, 2015 15:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericgj/d373d7e5d7131545938a to your computer and use it in GitHub Desktop.
Save ericgj/d373d7e5d7131545938a to your computer and use it in GitHub Desktop.
// A translation of Elm's StartApp to js + ramda + flyd
// for comparison see https://github.com/evancz/start-app/blob/2.0.1/src/StartApp.elm
// Comments welcome
var curry = require('ramda/src/curry');
var map = require('ramda/src/map');
var concat = require('ramda/src/concat');
var reduce = require('ramda/src/reduce');
var commute = require('ramda/src/commute');
var compose = require('ramda/src/compose');
var flip = require('ramda/src/flip');
var first = require('ramda/src/head');
var nth = require('ramda/src/nth')
, second = nth(1);
var flyd = require('flyd');
// perform side effects resolving/rejecting to action
// note this assumes ramda-fantasy futures for tasks; where errors are caught
// and passed into the reject branch.
var forkTaskTo = curry(function(target,task){
return task.fork(
function(rej){ if (rej instanceof Error) throw rej; return target(rej); },
function(res){ return target(res); }
);
});
/***
* Start app
*
* Note that config is similar to the Elm config (except untyped): an object with
* `init: state` // note: unlike Elm, it is not a (state, effects) tuple
* `update: action -> state -> (state, List (Task action))`
* `view: Stream action -> state -> vnode`
* `inputs: List (Stream action)`
*
* Note that Effects are modelled as `List (Task action)` -- not quite as
* general as they are in Elm.
*
* Returns an object of three streams:
* `state$ : Stream state`
* `vnode$ : Stream vnode` // whatever vnode your views render
* `tasks$ : Stream List (Task action)`
*/
module.exports = function start(config){
var action$ = flyd.stream();
// action -> (state, List (Task action)) -> (state, List (Task action))
var updateStep = function(action, tuple){
var state = first(tuple), accEffects = second(tuple);
var newtuple = config.update(action, state)
, newstate = first(newtuple), addEffects = second(newtuple);
return [newstate, concat(accEffects,addEffects)];
}
// List action -> (state, List (Task action)) -> (state, List (Task action))
var update = function(actions, tuple){
return reduce( flip(updateStep), [first(tuple),[]], actions);
}
// Note: converts `List (Stream action)` to `Stream (List action)`
// Note: prepends UI action stream to external inputs
// Stream (List action)
var input$ = commute(flyd.stream, concat([action$], config.inputs));
// Stream (state, List (Task action))
var stateAndTask$ = flyd.scan( flip(update), [config.init,[]], input$ );
// Stream state
var state$ = flyd.map( first, stateAndTask$ );
// Stream List (Task action)
var tasks$ = flyd.map( second, stateAndTask$ );
// Note: perform task side effects here
flyd.on( map(forkTaskTo(action$)), tasks$ );
return {
vnode$: flyd.map(config.view(action$), state$),
state$: state$,
tasks$: tasks$
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment