Last active
October 28, 2015 15:13
-
-
Save ericgj/d373d7e5d7131545938a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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