Created
August 1, 2016 03:35
-
-
Save saem/dc2a5bceaac97bed1892fc8fef0054ab to your computer and use it in GitHub Desktop.
Read about State, Action, Model (http://sam.js.org/), which is fashioned after TLA+'s concept of a step, I'm borrowing Paxos terminology: Propose, Accept, Learn as a step breakdown. Just doing some thinking out loud.
This file contains hidden or 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
| //@flow | |
| /** | |
| * The model, broken down into three parts (data, ui, & effects). | |
| * | |
| * data - canonical representation/interface with various backends. | |
| * ui - the UI state | |
| * effects - effects we want to trigger | |
| * | |
| * Model should probably be a parameterized type | |
| */ | |
| type Model = {data: any, ui: any, effects: any} | |
| var model: Model = { | |
| data: {}, // canonical data | |
| ui: {}, // intermediate UI state | |
| effects: {} // any effects that need to be sent off | |
| }; | |
| // Propose, accept, learn as a 'step' breakdown | |
| // the acceptor, is an accept function with the model & reducer parameters bound | |
| type ModelConsumer = (Model) => void; | |
| const propose = (acceptor: ModelConsumer) => (newModel: Model) => { | |
| acceptor(newModel); | |
| }; | |
| type Reducer = (Model, Model) => Model; | |
| const accept = (model: Model) => | |
| (reducer: Reducer) => | |
| (proposal: Model) => { | |
| // reducer does the real mutation logic to the model | |
| model = reducer(model, proposal); | |
| }; | |
| const learn = (learner: ModelConsumer) => (model: Model) => { | |
| learner(model); | |
| }; | |
| // Wiring together propose, accept, and learn | |
| const mutator = (reducer: Reducer) => | |
| (learner: ModelConsumer) => | |
| (oldModel: Model, proposal: Model) => { | |
| const model = reducer(oldModel, proposal); | |
| // make changes to the model, with some guidelines: | |
| // 1 - make any changes to data, if from the server | |
| // 2 - make any changes to ui, if ui state | |
| // 3 - enqueue any effects that we want fired off | |
| learner(model); | |
| }; | |
| const modelReducer: Reducer = (oldModel: Model, newModel: Model) => newModel; | |
| const learner: ModelConsumer = (learners: Array<) => (model: Model) => | |
| const proposer = propose(accept(model)(mutator(modelReducer)(learner))); | |
| const action = (proposer: ModelConsumer) => (fn: (Any) => Model) => (rawData) => { | |
| proposer(fn(rawData)); | |
| }; | |
| // @todo These learners need to be properly initialized with their dependencies | |
| // via partial function application, as above | |
| const render = (model) => { | |
| // rendering code here | |
| }; | |
| const effects = (model) => { | |
| // fire off effects such as HTTP requests here | |
| } | |
| const nextAction = (actions) => (model) => { | |
| // Invoke follow-on actions because of state changes | |
| }; | |
| // The nextAction should be the last of the learners | |
| const learners = [render, effects, nextAction]; |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There are some significant next steps that need to be thought through.
Model:
Mostly as a 'this seems nice' type thing I separated the UI, Data, and Effects parts of the model. It makes a lot of sense in some ways, but I'm not entirely convinced just yet. The model should also be parameterized by the three subsections. Also some attention needs to be paid to this whole thing, I see this pattern being recursively applied like the Elm Architecture, which means the whole mutation bit needs to be moved up one more layer, perhaps.
Reducers:
The various reducers can probably be cleaned up in that they shouldn't see the entire model, and currently, any attempt at proposing a new model state means knowing the whole model, rather than only the parts we're concerned about. I think the reducer pattern held in one area might make sense. But at the same time, it seems like a goofy dispatch/messaging layer that has its own set of issues.
Insights:
The only real insight thus far is that nextAction is just another learner, one that happens to be 'distinguished' in that it can fire further actions, and should (?) be the last of the learners.