These are chat archives for jdubray/sam

6th
Jul 2016
Jean-Jacques Dubray
@jdubray
Jul 06 2016 00:06
@jeffbski did you see the 10 min introduction? https://www.youtube.com/watch?v=lMgSS_b98S8
@jeffbski so just to be clear, after the model has accepted the proposal, we need to create the state representation and check if there are any next-action to do.
the view-model function is just here to show that in general the interface to the view components is decoupled from the model structure. It's hard to represent everything in a single formula, but it's important to have the best decoupling possible between the view and the model. The goal being that the view components are reusable across applications and easily interchangeable (Web, Mobile, ...).
Jean-Jacques Dubray
@jdubray
Jul 06 2016 00:11
So yes, S() is passes some information for the view to display, could be some HTML string, JSON, even a closure to be invoked in the view component.
Jeff Barczewski
@jeffbski
Jul 06 2016 00:30
@jdubray yes, I have watched the 10 minute intro and two of your longer presentation videos. Thanks for the clarifications.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 00:38

@mnichols I have trouble to see who decides the target state, is it the action (like in traditional state machine semantics) or is it the model.
When I look at this code:

go(args, target) {
            //1. mutate the target model here. It can consume the target's API, or whatever
            //2. determine the next state to transition based on the target state
            //3. do it
            target.doThatThing(args)
            if(target.isGoing) {

I am not sure if target is set from the beginning with isGoing = true or if the doThatThing method can change the property value isGoing to whatever it wants based on args?

I would say from a SAM perspective:
go = action
args = events
target = model
isGoing = state
... ?
but to be frank I find your model a bit convoluted unless I am missing something.
SAM is easy to reason about because each element of the pattern is 100% decoupled from the other.

  • Actions translate events into proposals
  • model accept proposals
  • state take the model, and compute state representation, nap()

nothing else is happening in SAM, each element does not know anything about the other element, the model does not know what is an action, which one exist, or what is a (control) state, a state presentation or a next-action

Jean-Jacques Dubray
@jdubray
Jul 06 2016 00:44
I have been using traditional State Machine semantics for all my career since I learned them in engineering school back in the 80s, until last year, when I realized they were just an approximation. These semantics are tuples like (Si, Aj, Si+1) : an action Aj transition from Si to Si+1. It is important to understand that it is not the action that decides of the resulting control state (Si+1)
That is the approximation, and this is why these semantics don't work well in general.
TLA+ / SAM semantics are a lot closer to the way people write code today.
Could you clarify which semantics you are using?

@jeffbski to continue on TLA+ (sorry was in a meeting):

TLA provides a mathematical foundation for describing systems. To write
speci cations, we need a complete language built atop that foundation. I initially
thought that this language should be some sort of abstract programming
language whose semantics would be based on TLA.

Mike Nichols
@mnichols
Jul 06 2016 00:47
yes after I posted that I realized I should have inverted flow. I appreciate your comments alot! in the past I have view publish a command that the control state machine handles and makes decisions. and then rerenders with the new presentation. i'll keep trying :)
Jean-Jacques Dubray
@jdubray
Jul 06 2016 00:48
@mnichols any time, I am happy to have good discussions, this how we get collectively better. I welcome people trying to find issues or similarities (hopefully they don't :-))
Jean-Jacques Dubray
@jdubray
Jul 06 2016 00:55
So I found what I was looking for this is the definition of an action in TLA+:
blob
the ' (prime) notation is very important in TLA+ because it keeps track of the next property values vs the current property values (proposals).
HCnxt2 is an ordinary mathematical formula, except that it contains primed as well as unprimed variables. Such a formula is called an action.
You can't make this up, for Redux an "action" is a data structure, this is wrong, sorry, fundamentally wrong, you cannot freely decide what an action it.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:02

The Model in TLA+ is called the "next-state-relation"

the next-state relation speci fies how the property values can change in any step

The last part of TLA+ is the "Temporal logic" (next-action)
Please note that in GUIs there are not a lot of "temporal logic". For instance TLA+ can be used to specify complex algorithms (e.g. AWS). In that case, there is always a "next-action"
Jeff Barczewski
@jeffbski
Jul 06 2016 01:08
So you are saying that because redux action creators don't have access to the current state then that's why they are fundamentally wrong? Otherwise I don't see much difference between the actions in the SAM examples, many of them simply pass a data structure too, like { incrementBy: 1 } which is not much different than { type: increment, payload: 1 }
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:14
Not action-creators are creators of "actions" (i.e. data structures). Redux sees an action as a data structure, that is what is wrong
you can't twist the semantics, you have to decide, is an action:
  • a data structure?
  • a function?
It's not because action-"creators" are functions that all the sudden Redux actions are "functions"
Jeff Barczewski
@jeffbski
Jul 06 2016 01:15
Just like I don't think state is a function.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:15
The reason why Dan used "data structures" is because of Time Travel
So what is it?
State is the equivalent of the "Temporal Logic" in TLA+, this is the hardest to represent. Actions and Model are pretty straightforward. Hard to disagree.
Jeff Barczewski
@jeffbski
Jul 06 2016 01:17
Well from the state-action behavior mentioned in the docs, I would assume state is a noun, it is a discrete set of values.
Mike Nichols
@mnichols
Jul 06 2016 01:18
I guess when I think to my CQRS stuff, we'd send commands that had precalculated (proposed) data which was marshaled via bus. The domain would handle the command and either reject it, or raise an event in response. I'm not sure if I am wrong trying to map this to that in my head
Jeff Barczewski
@jeffbski
Jul 06 2016 01:18
I see your point on action, it is more of an action representation or intent like you mentioned, the actual action is performed based off of the structure.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:20
All I am saying is that these choices have consequences. You pick a data structure, then you can't write logic around it, there is also the "sequence" for instance in the case of Redux, if I understand correctly you take the decision to dispatch very early.
One of the big consequences in Redux is that it's hard to decide where you factor API calls
not in the reducer, not in the "actions" since they are data structures,
then you start bringing heavy machinery like Sagas, when you do that you break the single state tree principle
Maybe I should not use the word "wrong"
It's "wrong" from a TLA+ perspective
Take Lee Byron from Facebook, he defines actions as:
blob
That's wrong too (from a TLA+ perspective). It is the next-state-relation that transform the application state, not the action.
Jeff Barczewski
@jeffbski
Jul 06 2016 01:22
which is inline with the state-action behavior, correct?
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:22
??
which?
Jeff Barczewski
@jeffbski
Jul 06 2016 01:23
our streams crossed, you already answered
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:23
At a very high level you could conclude that applying an action results in a new application state.
but is it the action that decides the the resulting application state? TLA+ says no, the action doesn't do that.
That's all I am saying, these choices are not arbitrary, if you pick some semantics, it has consequences.
@jeffbski the "state-action" behavior is always true, an action results in a new application state, the question is how? what is the most efficient factoring of the logic? what code do you put in the action, everything? like Lee Byron? nothing like Dan Abramov?
Jeff Barczewski
@jeffbski
Jul 06 2016 01:27
I assume in Lee's case he was talking about updating immutable data using an action fn to do that. If the model is basically being updated by an updateFn (or action as he must have said), then assuming the model doesn't reject the proposal then it will update. In the case of immutable there is not accept filter so it will always accept and thus the action fn will update to a new state (immutable object).
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:29
@jeffbski yes I understand where it is coming from, but again, from a TLA+ perspective this is not correct.
Jeff Barczewski
@jeffbski
Jul 06 2016 01:30
ok. I'm going to break for dinner, so I'll sleep on it and see if I gain more clarity. Thanks for the discussions.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:30
thank you!
@mnichols wrt CQRS, again, these are perfectly valid semantics but somewhat misaligned with TLA+
All I am saying really is that if you pick TLA+ semantics really good things happen.
Things that you don't see with other semantics.
Fred Daoud
@foxdonut
Jul 06 2016 01:34
@jdubray is the 10-minute intro to SAM extracted from the longer re-structuring of your presentation that you posted?
Mike Nichols
@mnichols
Jul 06 2016 01:35
I believe you @jdubray . I am working out a real feature for work trying to adopt what I see going on in your samples repo to grok it. I have to write to learn. The thing that trips me up is what the distinction is between having a view invoke an action which proposes data to the model for rejection or mutation versus sending a command and having the model receive the command (data) and either reject it or raise an event that carries the new state and mutates internal state. I'm sure it is in front of me but cant see it
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:38
@foxdonut yes, with some additional clarity based on our past discussions.
@mnichols it's really a question of factoring, sometime you need to put some logic in the action, that does not fit in the model or worse the view.
Once you start having this additional bucket of business logic, then interesting things start happening. I would argue that everything is a particular case of the TLA+ semantics, hence it is better to start with the TLA+ semantics.
Fred Daoud
@foxdonut
Jul 06 2016 01:40
@jdubray cool, well done :star:
Jean-Jacques Dubray
@jdubray
Jul 06 2016 01:43
@foxdonut one of my argument in particular is to say that the base React/Angular2 component model did not work because it fragments the model, we have to go to a single state tree, where view components ~functions.
When you do that, then it begs the question, assuming a single state tree model, what is the best organization of the business logic, since the React/Angular2 component model / ~Class is not the correct way to factor the business logic.
Mike Nichols
@mnichols
Jul 06 2016 07:15
I created my first jab at SAM with RiotJS for help here: https://github.com/mnichols/riotous-sam
I just fire up python -m SimpleHTTPServer 3001 to run it.
Anyways, I like where it is headed except a few things that I wonder:
  1. Assuming this is entirely client-side and if model represents the entirety of application state, what strategies/patterns would you recommend for splitting up into logical bits...something like a reducer+selector ? Dispatch to submodels that return their values that get attached as nodes on the larger, app model?
  2. Assuming a page with many components , do they all receive the same (monolithic) model or do you isolate components into mini-apps that interop somehow. This smells like an 'it depends' answer but I am sniffing around for what a large SPA that maintains alot of model state starts to feel like for scale and understandibility
Jean-Jacques Dubray
@jdubray
Jul 06 2016 07:18
@mnichols the pattern itself is composable in a parent/child relationship (child action can present data to the parent as needed, for instance on submit of a complex form/wizard).
Within the same pattern instance, the model is organized in "units of work" for the business logic and hierarchies of properties. Unlike Redux there is no immutability requirements, so you should not expect performance issues because you create a new instance of the model for each proposal.

do they all receive the same (monolithic) model or do you isolate components into mini-apps that interop somehow.

no, passing the model is kind of an anti-pattern, you really want the components to offer a functional / props interface just like React components. It's easier when you learn the pattern to pass the model, because it shows the data flow, but for real-world examples, you want the "theme" of components to be strictly decoupled from the model.

To achieve 100% decoupling, the wiring of the actions between the view components and SAM's actions is achieved via "intents".

You can find an example of intents in this Angular2 sample

intents: {
            edit: mount+'.actions.edit({',
            save: mount+'.actions.save({',
            delete: mount+'.actions.delete({',
            filter: mount+'.actions.filter({',
            search: `dispatch({__action:'search',`
        },

as you can see, I can do some interesting mix & match

Jean-Jacques Dubray
@jdubray
Jul 06 2016 07:24
The "intent" is wired in the component like this:
<td><button class="btn btn-danger" onclick="return ${intents['delete']}'id':'${person.id}'});">Delete</button></td>
or that:
return `<h1>App DEV COE Core Team List</h1>
                        <input  id="search" placeholder="Search..." autofocus autoGrow
                                #searchInput ${filterValue} (input)="${intents['search']}'id':'search','name':searchInput.value})"><br>` ;

I really want to emphasize that:

  • SAM is very very simple, but not simplistic at all, there is a robust formalism behind it
  • SAM is just a factoring of your code, it provides 3 main buckets to write your business logic and it's usually straightforward to know which bucket to use
  • SAM is making application state mutation a first class citizen of the programming model (as opposed to immutability)
  • SAM focuses on "what's allowed" at any point in time, no subscription to manage
  • SAM is "functional HTML" like React
  • SAM's code is isomorphic by design
  • SAM is wiring agnostic (JavaScript functions, RxJx, pub/sub, dispatcher ...)
    ...

Overall, that's a lot of value and all of it is delivered without a single line of framework / library
It's only when you need some advanced concepts like "hang back/cancellations" that you can start using some very light weight library.

Jean-Jacques Dubray
@jdubray
Jul 06 2016 07:31
In a typical SAM application you'll have lots of "actions", they are independent of each other, easy to reason about, just translating events into proposals
The model should be made as compact as possible and sliced and diced with "independent" sections. SAM usually does not require that the model keeps track of mini-state machines like "fetching" when you call an API. So that's a huge simplification of the app logic. These mini-state machines would otherwise proliferate in standard FRP code
The State function is what makes SPA manageable because you can generally decide quickly which (control) state you are in. The state representation is a json structure that's passed to the view:
{
    header: ...
    page1: ...
    component1: ...
    footer: ....
}
you can also pass "null" or omit properties if you know that you don't need to re-render that component. Overall you rarely need a virtual-dom library (IMHO)
Jean-Jacques Dubray
@jdubray
Jul 06 2016 07:37
You can see an Angular2 app structure here.
but it's pretty much the same decomposition principles regardless of the Web framework you use.
Fred Daoud
@foxdonut
Jul 06 2016 11:25
@mnichols also in thinking about your points 1. and 2. earlier :point_up: July 6, 2016 3:15 AM in Meiosis (a small helper library I am working on) you create components, each may have an equivalent to model.present so that if it makes sense for your application, you can split up some of that logic. Meiosis combines components so that you still have a single root model.
For 2., when you create components, you get back a V = f(M) function that you can call from a parent component. So, passing a component a subset of the root model is trivial, you could call e.g. someChildComponent(model.sub.part).
Fred Daoud
@foxdonut
Jul 06 2016 11:35
Here is just one example of calling components as functions. I am passing the full model to both but I will update it to pass sub-parts of the model, because I agree with you that it is an important capability.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 11:51
@mnichols Stamps sound like a very interesting concept. Have you used it at scale?
https://github.com/stampit-org/stampit
Fred Daoud
@foxdonut
Jul 06 2016 12:24
agree, looks nice
@mnichols also I noticed you're using Riot. That was one of the view libraries I considered adding to Meiosis (right now I have React, Mithril, Snabbdom). What made me shy away from Riot was that views are tags, so markup with special syntax, similar to templates. I prefer writing regular JavaScript instead of special template syntax. Would be interested in hearing your thoughts on this. Also, have you tried other view libraries? Did you find Riot had advantages over others?
Jean-Jacques Dubray
@jdubray
Jul 06 2016 13:09
+1
Jean-Jacques Dubray
@jdubray
Jul 06 2016 13:24
@jeffbski
I found another great example of "actions" in TLA+ (p81 of the book):
blob
Jean-Jacques Dubray
@jdubray
Jul 06 2016 13:42

as you can see, the model, will then "accept" data (let's assume there are no mechanical issues to deal with), so presenting {"a","shift"} would then result in the model accepting "A". Alternatively, you could say the Model should not know anything about the design of the keyboard and charset.
Of course the next action would for instance be a spelling check.

There is no "right" or "wrong" design, but the separation proposal/acceptor makes the intent of the designer/developer very clear, and much more maintainable over time.

Jean-Jacques Dubray
@jdubray
Jul 06 2016 14:17
In any case, you don't want the model to understand the meaning of events? It's best to "translate" events into "units-of-work" (updates) to the model. Several types of "events" can lead to the same unit of work.
Mike Nichols
@mnichols
Jul 06 2016 14:57
@foxdonut @jdubray I really appreciate your replies. I am going to spend time looking through Meiosis. RE: stampit...yes I have used it in many large SPA apps. It is dead simple and really simplifies composition and reinforces my team away from classical construction.
RE: riotjs...I reach for RiotJS over React because it more closely resembles HTML which is a win for the team I am on (not JS or front-end enthusiasts). The syntax is very simple to learn and new devs have been able to grok it quickly. Also it is much smaller, has an easier API to learn and operates on the DOM. I have banged up against bugs in the synthetic events in React before that left a bad taste in my mouth.
I agree that virtual DOM is oversold.
The argument I see coming is that there appears to be duplication in logic between the state 'model' has and the interpretation yielded by the 'state'. For example, in my example I have 'displayProgress' in my "model" and then the 'state' interprets that to render the progress indicator. It seems like 'model' shouldnt know about view affordances like this and the 'state' would be better served to sniff something like 'model.loading' ?
@jdubray regarding the 'intents'...I saw that and it felt ceremonial. I was thinking it would be better to dispatch actions on a 'actions' channel on a bus that (synchronously) invokes the top-level 'present'. It'd be simple to run these in a unit of work and would offer the same decoupling of view topics ('intents') to action callbacks.
It seems like the 'present' should always_ be asynchronous to avoid releasing zalgo. If a action is being proposed, subsequent proposals (action invocations) would be queued on the nextTick. Importantly, though, nap would _not be queued but would be on the inside of the current action's loop.
Jeff Barczewski
@jeffbski
Jul 06 2016 15:00
@jdubray From the p81 you referenced, I notice that he suggests that we can refer to the "state" of the keyboard as {}. So back to what we discussed yesterday "state" is a noun, represented as data not a function. So if we are going to have consistent semantics, one needs to do so through out, not picking and choosing. As for actions, I see the differences in how the word action is being used by different people. Lee was using it to describe a function which converts one immutable into another, Flux and Redux use action to describe the command object which describes the action that it wants to be performed (even though the actual transformation is handled elsewhere). The command pattern (https://en.wikipedia.org/wiki/Command_pattern) mentions that Action Object is a common synonym for a Command Object. So it might be more correct for flux/redux to use the term Action Object or Command Object, but in use sometimes things are shortened Command Object becomes Command, Action Object becomes Action. I would even question whether SAM's use of Action is entirely correct, since it is a function that accepts a dataset and returns a dataset, yet the dataset has yet to be accepted by the model to cause a state transition. So SAM's action function by itself does not bring about a state transition, the proposal still needs to be accepted by the model for the transition to have occurred.
So I guess my point here is it is a worthy thing to try to use the proper semantics and naming but we all know that is one of the most difficult things in programming. So I don't know that we should be so quick to judge or land harsh words about naming choices made by this pattern or framework. I think there are mistakes or inconsistent choices all over our industry. So we should do the best we can to make good choices and name things well. We most likely will make mistakes along the way but we should at least be consistent.
Jeff Barczewski
@jeffbski
Jul 06 2016 15:06
I mean that we need to be consistent in our own project.
Mike Nichols
@mnichols
Jul 06 2016 15:07
@jeffbski how would you feel about 'proposal' instead of 'action'? To me the 'present' interface was confusing since the earlier discussion called the conversation a 'proposal'. It seemed to me like model.propose(data) was expected and the 'action' was the representation of that 'proposal'.
So views bind to intents which map to proposals
Jeff Barczewski
@jeffbski
Jul 06 2016 15:10
Well proposal sounds to me as the data representation being passed to the model. If you are talking about the data then I think proposal is good, if you are referring to the function that created the proposal then maybe action or similar is better.
The better we do with naming things consistently with how our user base (programmers) use them, the less confusion we create and they learn things quickly. The more there is consistency with common use then the less context switching one has to do when going between projects/frameworks.
Mike Nichols
@mnichols
Jul 06 2016 15:15
proposer ? Crap, now I have Julia Roberts in my mind because of Indecent Proposal. Showing my age
Jean-Jacques Dubray
@jdubray
Jul 06 2016 15:16

@jeffbski

"state" is a noun, represented as data not a function.

I actually discussed this very point with Dr. Lamport he suggested to use control state (which would still be a noun) for what I reference as State. Now, there is the "pattern" which calls for a function there "State()" is.
Please understand that "words" cannot define semantics (we had this discussion last week). Words refer to semantics and we have a finite number of words. State being attached already to about 10 semantics.

Jeff Barczewski
@jeffbski
Jul 06 2016 15:18
Agreed. Many semantics, less number of words.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 15:19
Dr Lamport use a different name for 'control state' he uses "program counter" (pc) in reference to Von Neumann. You will notice in all TLA+ examples "control state" is a variable, always called pc.
Mike Nichols
@mnichols
Jul 06 2016 15:19
It seems like a language is formed in the docs with 'proposal', 'learner', and so on. Since these describe the roles the parts play it might help disambiguate from other patterns. Also it would express the intent since 'proposals' could be rejected whereas an 'action' feels like it can't.
Jeff Barczewski
@jeffbski
Jul 06 2016 15:19
@mnichols proposer could refer to the function that creates the proposal, but it also somewhat sounds like the originator of the proposal if we were in an OO context. So I don't know what is the best term.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 15:21
PAL comes from the Paxos protocol, also invented by Dr Lamport. Originally I didn't bring Paxos into the mix, but it seemed that it was easier for people to understand the patterns in terms of proposers / acceptors.
The beauty of the lineage with Paxos is that you can see the potential of the pattern to create really a distributed Model /application state, with eventual consistency.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 15:36
@jeffbski incidentally, {"a"} is the "state representation" of the keyboard not it's "state". The state representation is how the application/system state is rendered to the world outside the system (here the system is the keyboard).
So I'll stay on my position that the goal of the "state function" is to create the state representation of the model. This is often based on the "control state" of the system which itself is derived from the property values of the model.
Fred Daoud
@foxdonut
Jul 06 2016 15:45
@mnichols in your example, where does possum.js come from? it's not in the github repo.
Mike Nichols
@mnichols
Jul 06 2016 15:48
eek..that is a bug :) I thought it was relevant but once I implemented the pattern I saw it doesnt fit so well (except perhaps as a Model implementation later) I removed it from repo but forgot the script tag
Fred Daoud
@foxdonut
Jul 06 2016 15:55
ah ok no problem!
Mike Nichols
@mnichols
Jul 06 2016 15:57
@jdubray why did the name of this pattern become SAM instead of PAL... why not reuse the names found in Paxos? Others on my team already are asking :)
Jean-Jacques Dubray
@jdubray
Jul 06 2016 15:59
ah ah, it was suggested, I'll put it up for a vote soon.
Vincent Jo
@chiwoojo
Jul 06 2016 16:14
hey JJ, I was wondering if an action could call another action? If in the future I run into that situation, there is something I can do better to structure my code right?
Jean-Jacques Dubray
@jdubray
Jul 06 2016 16:18
No, strictly forbidden, the semantics of an action is strictly to present a proposal to the model
you can do a functional composition A(data) = B(C(data)) but this kind of composition has only one proposal, it's just logically it is easier to write your code (more reusable) as a function composition, but semantically, this is just one action.
Fred Daoud
@foxdonut
Jul 06 2016 16:22
@mnichols not sure what it's supposed to do, but when I click on GET READY, nothing happens..
Mike Nichols
@mnichols
Jul 06 2016 16:28
@foxdonut well...that's embarrassing. It was late when I pushed it up last night. I'll patch it now to figure out what I broke :(
Vincent Jo
@chiwoojo
Jul 06 2016 16:29
thanks JJ
that makes sense
composition is fine as long as it is making one proposal
Fred Daoud
@foxdonut
Jul 06 2016 16:32
@mnichols thanks! I did notice that if(startable) { should be if(startable(model)) { and if(started) { should be if(started(model)) {, but otherwise I don't know enough about how Riot works to figure out why it's not working.
Mike Nichols
@mnichols
Jul 06 2016 16:33
@foxdonut lol...i am on it
Jean-Jacques Dubray
@jdubray
Jul 06 2016 16:33
@chiwoojo In a way the C action presents it's proposal to the B action, so we are still in the logic of the pattern, but I prefer talking about function composition, or composite action, rather than "two" actions.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 16:39
@chiwoojo StampIt sounds interesting https://github.com/stampit-org/stampit
Mike Nichols
@mnichols
Jul 06 2016 16:40
@foxdonut I just pushed a fix. I had renamed the repo and must have lost something last night. When you click 'GET READY' it should demonstrate doing an nextAction and asynchronous call. When you click 'Start The Show' it does a slide show but without slides (for now) . I was trying to get a handle how nextAction works along with asynchrony using a kind of slideshow. I am going to improve on it
Jean-Jacques Dubray
@jdubray
Jul 06 2016 16:43
@chiwoojo if you want to see what's wrong with the way think about "multiple actions" you could look at this post: http://jamesknelson.com/can-i-dispatch-multiple-actions-from-redux-action-creators/
let’s consider the problem of handling a submit event from a form. In response, you’ll want to do multiple things. For example: RESET your form’s view model, POST your form’s data to the server, and also NAVIGATE to another route.
Jeff Barczewski
@jeffbski
Jul 06 2016 16:45
Just be aware that StampIt does not build objects using the traditional prototype mechanism, so you are going to have a performance penalty in modern JS engines like V8. They are optimized for objects using prototypes. If you are using StampIt in userland code then it might be fine, but if I was building a lower level library that is going to see heavy use, I would recommend either not using it or at least do some benchmarking.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 16:45
These can be viewed as "actions" but not quite
1/ RESET -> model accept some values and updates it's property values
2/ POST form data is not "an action", IMHO this is handled in the model
3/ NAVIGATE -> new state representation
semantic imprecisions lead to spaghetti code
SAM provides some clear guidelines as to how to do that.
Mike Nichols
@mnichols
Jul 06 2016 16:49
@jeffbski That has come up before and while I think that used to be more of a concern, I'd respectfully argue it is optimizing in the wrong place and today's engines don't really yield that much of a benefit. Nonetheless I agree it should be acknowledged and benchmarked like you suggested. Eric Eliot (the author) deals with that and some other objections to factory method in some of his writings. The ROI on prototypal inheritance over classical pays off in the apps I've done.
Vincent Jo
@chiwoojo
Jul 06 2016 16:57
@jdubray will be checking out stampit throughout the day to check it out; seems like a new way to build objects instead of the known JS patterns
Fred Daoud
@foxdonut
Jul 06 2016 17:14
@mnichols works now, thank you :thumbsup:
Jeff Barczewski
@jeffbski
Jul 06 2016 17:30
I know that's the argument that Eric and others have made, but the performance penalty is significant and shouldn't be ignored. That's why I say high level userland stuff it is possibly ok. I would not want to use it in a low level library which creates millions of objects. A few years ago my team did some real world style benchmarks with Object.create vs new and it was pretty eye-opening. It sounds like firefox might have improved things since then closing the gap, but others are still sighting 33X or more difference in perf (that's not 33%, but 3300%). People have made the argument that you can optimize later when needed, but you really can't completely ignore it either, at least in important areas. I know of a few IT projects that chose to ignore performance and were abandoned when they finally got around to doing that since it was going to need a full rewrite. So while you shouldn't spend your time in the weeds microptimizing you need to keep your eye on the big picture and watch how your features are impacting performance. Running your code through real use-like benchmarks is the way to go. I tend to agree about using better patterns rather than always optimizing, for instance I like using map and reduce rather than a "for" loop even though they are slower, but you'll see that any heavily used low level library is going to use "for" loops instead because anything used a million times adds up. So buyer beware. Do your own real benchmarks and if it is reasonable then go with it.
Mike Nichols
@mnichols
Jul 06 2016 17:31
Good stuff @jeffbski !
Jeff Barczewski
@jeffbski
Jul 06 2016 17:31
:-)
Jean-Jacques Dubray
@jdubray
Jul 06 2016 17:45
@jeffbski how do these numbers impact approaches based on an immutable model?
I am doing a presentation to a large company based in Seattle friday and I have heard repeatedly that Redux's Reducer (immutability) was something to watch for, but I don't have any numbers to back it up.
Jeff Barczewski
@jeffbski
Jul 06 2016 17:47
You really have to do real benchmarking again because it is all over the map and depends on your particular use case. It matters how much thrashing you have, how big the objects are, how many objects, what percentage of read vs write, etc.
Also things like whether you have arrays of objects or just maps, etc.
With Redux you could use plain objects (being careful to create new ones), immutablejs, FB immutable helpers, seamless-immutable, TIMM, sprout-data.
Some of the immutable helpers try to do a little bit of reuse depending on what you are updating.
Fred Daoud
@foxdonut
Jul 06 2016 17:52
@mnichols forgive me if I misunderstand how Riot works, but it seems to me that in your example, the logic of choosing the representation does not really choose which view to render. Instead, the markup always includes all the views (tags) and the show="..." is what actually decides what to display. Am I mistaken? FWIW, with these changes, the example seems to work the same.
diff --git a/app.js b/app.js
index 61ea871..208f9b7 100644
--- a/app.js
+++ b/app.js
@@ -13,7 +13,7 @@ const view = stampit()
 .init(function(){
     let tags

-    const mountOrUpdate = (key, model) => {
+    const mountOrUpdate = (model) => {
         tags.forEach( t => {
             t.update({ model })
         } )
@@ -28,10 +28,10 @@ const view = stampit()
             return tags
         })
     }
-    this.startable = mountOrUpdate.bind(this, 'startable')
-    this.started = mountOrUpdate.bind(this, 'started')
-    this.progress = mountOrUpdate.bind(this, 'progress-indicator')
-    this.oops = mountOrUpdate.bind(this, 'oops')
+    this.startable = mountOrUpdate
+    this.started = mountOrUpdate
+    this.progress = mountOrUpdate
+    this.oops = mountOrUpdate
 })
Mike Nichols
@mnichols
Jul 06 2016 17:55
@foxdonut yeh I got lazy towards the end so there is some cruft. Right now I was letting the riot container tag (startable) do the toggling and in a sense i could argue that a riot tag could be the state/responder in that regard I think. The benefit here was using riot's update to push diffed changes into respective views.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 17:56
@jeffbski makes sense, thank you.
Jeff Barczewski
@jeffbski
Jul 06 2016 17:56
@jdubray after trying the various methods of creating immutable data, I like immutable.js and I also like using the timm immutable helper for plain objects. So if I think that my data is going to benefit from a persistent structure I'd use Immutable.js otherwise I'd probably go with the timm helpers and plain objects. Mori is also a good choice for certain use cases but it is also larger and has a much different api.
Mike Nichols
@mnichols
Jul 06 2016 17:57
@foxdonut @jdubray I am trying to wrap my head around multiple components contributing to the overall Model using their own present interface. It seems like the top-level Model would have ultimately have little of its own state and each call to its present would delegate into each leaf models' present before the final render call. Is that the right track?
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:01
@mnichols I'd rather think of the model as a collection of "units-of-work", when it receives a proposal, it needs to quickly figure out which UoW to carry out.
I don't think there is a big benefit breaking down the model along data lines, though intuitively that's what people would think about doing first
Jeff Barczewski
@jeffbski
Jul 06 2016 18:02
@jdubray Also note that if people are using immutable.js then they need to be using it properly and not always converting it back and forth to plain objects. For instance one could call toJS on an immutablejs list then use the array to iterate or one could take advantage of the fact that immutablejs implements iterator protocol and just map over it and access properties directly without creating the temporary array. Obviously doing things like that will kill performance. From what I have seen Immutable.js holds up pretty well in many use cases, but timm would be my strategy if I need to use plain objects. I had though seamless-immutable looked nice too, but in further reading, I don't think I could recommend it, performance is pretty bad even using production version (which doesn't object freeze).
I meant creating the temporary arrays will kill performance, not iterating in place.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:03
yes it makes sense
Mike Nichols
@mnichols
Jul 06 2016 18:04
@jdubray i am considering this for testing isolation and splitting into components . it seems like multiple child models might accept a proposal but can still roll into a single unit of work
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:05
sure, that works, I'd argue that if you break down the UoW it might be harder to reason about.
Usually, UoW should be relatively small in size, it's more about integrity of the model (accepting data). Validation and enrichment happens in the action.
Mike Nichols
@mnichols
Jul 06 2016 18:10

Hmm..I'm thinking along the lines of

model.present = (proposal) => { 
return Promise.resolve(proposal).each(children.presents).then(this.state.render,handleRejection) 
}

Each child is responsible for maintaining its integrity as its part of the model

Jeff Barczewski
@jeffbski
Jul 06 2016 18:10
@jdubray As I mentioned before, doing real use case benchmark is the best, but I did find the information in timm's site helpful and providing some guidance as to which way I might go based on a particular use case. He summarizes things based on the small set of benchmarks he did. Take with a grain of salt as with any benchmarking. http://guigrpa.github.io/timm/
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:13
@mnichols there should be only one UoW responding to a proposal. I am not sure this kind of implementation would be helpful. You could argue that you don't which subelement of the model will respond to the proposal, I would argue that if each children were expected to do some work, that'd be an anti-pattern (in general).
Mike Nichols
@mnichols
Jul 06 2016 18:14
So if using composition, actions would likely carry along some kind of affordance to indicate 'which' model they target?
Fred Daoud
@foxdonut
Jul 06 2016 18:15
@mnichols in Meiosis you can have a single model.present (it's called receive, as in, receive a proposal) in a top-level component, and/or multiple receive functions, in separate components. In the latter case, I agree with JJ, only one should respond to a proposal. Meiosis wires everything together into a single root model.
@mnichols as for what you said about the Riot example -- fair enough :) I might try to add Riot to Meiosis, perhaps that would be helpful for you to experiment with it.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:17
@mnichols yes, you can still have a variety of strategies to route to the proper unit of work. There is not one/a single solution that works.
It's a bit like "stored proc" vs "transactions", the stored proc (action) will lead to a specific DB transaction (unit of work). In SAM the two are as decoupled as they can be, but obviously you need to wire them in one way or another.
Mike Nichols
@mnichols
Jul 06 2016 18:18
@foxdonut it would be great to see how you interpret its usage! thanks. In the case of multiple components, it sounds like you are saying an action is directly handled by the component 'model' but then causes a render on the top-level state/learner. Almost like a component could have its own channel the action is published on
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:19
I may be a bit extreme but I tend to think that we cannot find a "component" model, it's better to think about actions, model/children, state/children and view/theme/components in isolations
Fred Daoud
@foxdonut
Jul 06 2016 18:20
@mnichols indeed, you are correct.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:20
I do not believe that it's practical to think of business logic as one component with a few actions, some parts of the model, a few states, and one view component.
I like @foxdonut approach with Meiosis because you "assemble" the elements as needed (I call that wire), but that's different compared to a React of Angular component, where the parts of the component (data/methods) are trapped in it.
Mike Nichols
@mnichols
Jul 06 2016 18:21
@jdubray i am trying to picture how that is understandable on a page with alot of disparate view components or how i can promote reuse of these view components across different apps
I'd still hang each components 'model' state on the top-level model but enforce the 'one proposal handler' rule
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:22
IMHO that's not hard at all, that's why I create "themes" the state representation and the view assemble the theme's view components into the page.
Fred Daoud
@foxdonut
Jul 06 2016 18:23
@jdubray I agree with you, I also prefer having a single root model, actions, state, and isolated views. This is what I did in the https://github.com/foxdonut/meiosis-examples/tree/master/examples/todo-list example. The root is in todoMain/, and the todoForm + todoList are just view functions.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:23
yes, that's really the right approach IMHO. This is very powerful.
Mike Nichols
@mnichols
Jul 06 2016 18:23
are there examples of tests for any of the sam-samples or meiosis?
When I start to have 10 panels on a SPA it just feels like that is alot of conditional logic to try to grok
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:24
to the best of my knowledge only one sample uses chai / mocha
@mnichols I don't think so, the state representation is a JSON structure that would provide the corresponding state presentation for each element
Fred Daoud
@foxdonut
Jul 06 2016 18:26
I just thought it would be good to support the idea of having a component with its own receive etc. because sometimes it might make sense: in particular, when all the code associated to that component does not belong in the application if the component is not present. In other words, if you remove the component, it's easy to remove all the associated code, instead of fishing for it in the root model/state/etc. The component is still part of the bigger picture, there is still a single root model.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:26
however it is more or less centrally decided (parent/child is ok here of course). Again the key is to quickly understand the "control" state from the model property value so you can route to the correct state representation functions. (if (ready) { ... })
Fred Daoud
@foxdonut
Jul 06 2016 18:26
@mnichols I have unit tests for meiosis itself, but not yet for the examples. it's on the radar, though. :)
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:27
@foxdonut I would rather treat it as parent/child SAM instances.
Fred Daoud
@foxdonut
Jul 06 2016 18:27
@jdubray don't you lose the single root model if you do that?
Mike Nichols
@mnichols
Jul 06 2016 18:28
@foxdonut 'element' is , for example, a list of companies, a header, a spinner, etc?
Jean-Jacques Dubray
@jdubray
Jul 06 2016 18:29
it's more the "wizard" example, in other words, if you create a child SAM instance, it is truly isolated (like a separate system) and then would trigger at some point an action to present data to the parent
So yes, you lose it, but it's logically isolated.
Fred Daoud
@foxdonut
Jul 06 2016 18:30
@mnichols pardon me, what are you referring to when you say 'element'?
Mike Nichols
@mnichols
Jul 06 2016 18:30
ah so the wizard has its own loop but then calls out to the top level model. @foxdonut you had said 'I don't think so, the state representation is a JSON structure that would provide the corresponding state presentation for each element'
Fred Daoud
@foxdonut
Jul 06 2016 18:31
That was JJ :)
Mike Nichols
@mnichols
Jul 06 2016 18:31
oops...@jdubray what is 'element' here: "I don't think so, the state representation is a JSON structure that would provide the corresponding state presentation for each element"
Fred Daoud
@foxdonut
Jul 06 2016 18:31
@jdubray I understand, that makes sense. I think it is a different situation than what I am thinking.
Mike Nichols
@mnichols
Jul 06 2016 18:34
We need to reuse view components across multiple pages, carrying their own state contracts. It seems like being able to attach to the top-level model but carry their own present interface is the only way to gain this kind of reuse without embedding it into a single model.
Fred Daoud
@foxdonut
Jul 06 2016 18:57
@mnichols yes, exactly
Mike Nichols
@mnichols
Jul 06 2016 18:57
For example, that little repo I posted is extracted from a 'slideshow' component we need to use on a variety of pages...hence playing part of differnet parent models. Embedding that inside the top-level model would lead to duplication of tests, logic, etc I think. So the next logical step is to have the top-level handle any present and delegate to the slidehow model. If the child is working on a proposal while the parent receives another proposal we get into confusing behavior so it seems like all proposals need to first be routed thru the parent.
Jean-Jacques Dubray
@jdubray
Jul 06 2016 19:00
@mnichols the view components can be mounted several times on the same page/activity or different pages/activities. They are like React stateless components/functions. Not sure I understand why you think they cannot be reused. The State function orchestrates the computation of the state representation. That's were all the logic will be.
State -> what get displayed
View -> where it gets displayed
Theme (View Components) -> How it gets displayed
the "elements" I was talking about were the View elements (where it gets displayed)
Fred Daoud
@foxdonut
Jul 06 2016 19:01
@mnichols In Meiosis all components' receive functions get called, so the parent does not need to have "knowledge" of children and delegate to them.
Mike Nichols
@mnichols
Jul 06 2016 19:05
ok. i'll chew on those remarks a while. thanks for the inputs fellas
Jean-Jacques Dubray
@jdubray
Jul 06 2016 19:06
Thank you for the discussion
Jean-Jacques Dubray
@jdubray
Jul 06 2016 19:16

Sorry, I found that:

Luckily, the folks over at facebook have realized this and have blessed us with a new add-on, ReactCSSTransitionGroup, to solve the problem. ReactCSSTransitionGroup is itself a component in which you can nest child components. Any child component that mounts will undergo the ‘enter’ transition right after it mounts, and any child component that unmounts will undergo a leave transition before it unmounts.

it made me laugh.

Is someone keeping track of how many of these things you need?
Mike Nichols
@mnichols
Jul 06 2016 19:17
Yeh that is the stuff I am trying to shield my fullstack devs from to avoid getting burned at the stake for suggesting all the complexity that emerged over the last year
Jean-Jacques Dubray
@jdubray
Jul 06 2016 19:18
From a "state representation" perspective, "animations" are functions, while other parts of the state representation are static data structure or even strings
In SAM, the State representation would contain an "animation" function that would be passed to the stateless component.
The function could do what it needs to do (including using timers) to the component. As a former Objective-C dev (~10 years), looks like someone in the react team fell in love with its event model.
They are completely out of control.
:smile:
Mike Nichols
@mnichols
Jul 06 2016 19:27
the lean api and simple approach is why i reach for RiotJS. they are pretty rigid about extending the api
Jean-Jacques Dubray
@jdubray
Jul 06 2016 19:30
+1
Fred Daoud
@foxdonut
Jul 06 2016 20:46
@mnichols how do you dynamically render a view in Riot?
Mike Nichols
@mnichols
Jul 06 2016 20:50
I believe you create a tag instance, then use riot.compile(tag, true); which returns a the JS for the tag. Ordinarily you just 'mount' a tag and you get tag instances back which you interact with (either inside or outside the tag). So when you call update it is like calling setState on the component I believe.
Fred Daoud
@foxdonut
Jul 06 2016 21:14
@mnichols ok but how do you actually render the tag dynamically? i.e. <parent><something></something></parent> but something is a dynamic tag name, and after an event, the tag name would change to a different tag.
Mike Nichols
@mnichols
Jul 06 2016 21:43
ah like injecting a tag into a parent? hmmm....i need to read their docs or fiddle to see how. You should look at riot-tag like <div riot-tag='something'></div>. If you change that to an expression <div riot-tag={myNewTag}></div> then it might achieve what you are after. I havent tried yet
riot mounts elements in-place, so it seems like it would just mount the tag name at opts.myNewTag if you assign to this.myNewTag eg:
<parent>
   <div riot-tag={myNewTag}></div>
   <script>
      this.myNewTag = opts.myNewTag // put on context...will respond to update calls
   </script>
</parent>
Mike Nichols
@mnichols
Jul 06 2016 21:50
hmmm...i just tried to do that and it doesnt mount. The expression is replaced but it didnt mount the tag in there. I'll check on the riot issues to see if it is supported
Here is a report that it doesnt work: riot/riot#1540
Fred Daoud
@foxdonut
Jul 06 2016 21:57
Thank you for looking into it @mnichols I appreciate it! I did somehow manage to get it working (sort of) but it's really messy and probably not the right way to do it.
Not sure if this would help: https://github.com/crisward/riot-subtag
To get it working I had to put my data in an array, loop over the array in the template, call <raw> on each iteration, where <raw> is
<raw>
  if (this.data && this.data.tag) {
    riot.mount(this.root, this.data.tag, this.data);
  }
  <div></div>
</raw>
(And yes, that empty <div> seems necessary, else nothing renders)
Mike Nichols
@mnichols
Jul 06 2016 22:31
not too bad of a hack imo
It seems like with riot, you'd get rid of the representation call and simply view.display('thestatekey', dataFromState) ... so push instead of ask-then-display
or maybe representation returns the tag name and that is it
Mike Nichols
@mnichols
Jul 06 2016 22:41
bah...ignore me...i got my wires crossed between implementing 'state' and 'view'