These are chat archives for jdubray/sam

28th
May 2016
Daniel Neveux
@dagatsoin
May 28 2016 07:56
and if you want to have an optimistic ui, you could run a SAM instance on the server and a SAM instance on the client. When a client triggers an action, it treats it without waiting for the server answer. Meanwhile, the same action is sent over websocket, treated by the server and goes back to the client with the answer.
Jean-Jacques Dubray
@jdubray
May 28 2016 08:46
@dagatsoin yes, in general I would say there are both a client and server SAM instance
STAR is a SAM implementation with State-Machine semantics.
T stands for Type (~Model)
Jean-Jacques Dubray
@jdubray
May 28 2016 08:51

@devinivy @cloutiy You could also either:

  • poll the "State" in SAM (each client would then share the same state representation)
  • poll the model with the State function running on each client, in that case each client could have a different State representation. You would just have to watch for nap(), though logically you could have also a nap() specific function

SAM's implementation are naturally isomorphic, here are all the different configurations for the elements of the pattern.

Anton Stoychev
@antitoxic
May 28 2016 17:50

Hello! I had a read through the initial SAM article and the topics in http://sam.js.org/ . This is all new to me and happened just a month after I got confident with React and Redux (no relay/graphql). Few things about SAM are not clear to me. Having my React+Redux background:

  1. I can see the value of passing actions directly to components. I was struggling with redux messages anyway.
  2. I can't help it but see similarity between derived computed data that SAM control state

    if (state.ready(model)) { // derived computed data
    representation = state.view.ready(model); // the view will also call pure-model functions to determine properties of the components (green/red for example)
    }

    do and Redux container components that basically wrap a React component and map Model's values to properties. Is it against SAM pattern to use redux containers or is this the intended way to implement SAM pattern in React+Redux scenario? Does SAM pattern requires a single state-control function that decides everything that is to be rendered? If I use Redux container components, then where should my nap() be called?

  3. If the models persist some of the state via async/ajax call., then how do I represent a "loading..." state? Should I put a flag saving_in_progress: true in my model? (this doesn't feel right). Same kind of question goes for cases where form validation is needed - do I store a key like username_error='not_unique' in the model?
Jean-Jacques Dubray
@jdubray
May 28 2016 19:41
@antitoxic Anton, thank you for joining the discussion. @509dave16 has built a really nice sample with React/Redux.
To answer your questions:
  1. Yes, it has to be one of upmost goals of your Front-End architecture. All view components should be decoupled 100% from your application. The "theme" concept is one of the most important achievements of SAM.
David Fall
@509dave16
May 28 2016 19:46
@jdubray You linked to my old version that doesn't use the SAM pattern. But there could be benefits from comparing both of them: https://github.com/509dave16/sam-tic-tac-toe
Jean-Jacques Dubray
@jdubray
May 28 2016 19:47
  1. SAM is based on TLA+ semantics which are fundamental to computing, so there will always be some similarities once you start getting close to action, mutation and state machines. However, I would argue that TLA+ semantics are the most adequate.
  2. I see the same similarities. There are however some differences, SAM's State function (which main goal is to create the "State Representation" just like Containers) have also some role related to "control state" which Redux ignores completely. Namely, the State function is responsible for invoking the next-action-predicate (nap) and optionally, but highly recommended, for computing the allowed actions in a given control state.
This message was deleted
SAM makes a difference between the Model as a set of property values and the (control) State which interpret these property values to derive which control State you application is in (an example of control state is "started" or "parked" for a car) and from there derive nap() and allowed actions.
Jean-Jacques Dubray
@jdubray
May 28 2016 19:52
One of the key problems of the Redux architecture is that it has no way to keep track of what is supposed to happen at any given time. They are using Sagas for that, but that's the completely wrong way to go about it because you are in essence breaking Redux (and SAM) principle #1 which is a single state tree.
thank you @509dave16, I'll take a look later today!
Jean-Jacques Dubray
@jdubray
May 28 2016 19:58

Is it against SAM pattern to use redux containers or is this the intended way to implement SAM pattern in React+Redux scenario?

I would say it's kind of against since there will be multiple control states which is not SAM's intent for the State function. There is generally one control state.

Does SAM pattern requires a single state-control function that decides everything that is to be rendered?

Yes, some systems may be in a "composite state" or there could be logically separated control states, but in general there is only one.

If I use Redux container components, then where should my nap() be called?

This is another big problem I see, this time with the React architecture, since you are not controlling the rendering of the Model into a State Representation, it makes it harder (not impossible) to be smart about what happens between the time you mutate the model and the time the State Representation is rendered.

The simplest implementation would be to have a single container so every model mutation will provide a fresh set of props to trigger the entire rendering. nap() and allowed actions could be computed in componentDidUpdate
Jean-Jacques Dubray
@jdubray
May 28 2016 20:09

If the models persist some of the state via async/ajax call., then how do I represent a "loading..." state? Should I put a flag saving_in_progress: true in my model? (this doesn't feel right). Same kind of question goes for cases where form validation is needed - do I store a key like username_error='not_unique' in the model?

This would be no different than in Redux or any unidirectional dataflow architecture. Depending on the complexity of the scenario, you may want to use parent/child instances. For instance a complex form (or wizard) would certainly mandate it's own child SAM instance, it is only when you are ready to submit the form that you would then invoke the action on the parent instance.

With respect to the spinner, I would have the view start the spinner upon invoking the action. That's a bit of a "bend" in terms a single state tree. Since obviously you have some application state that is unknown to the model. The view would call the action passing the div responsible for the spinner (assuming you use a CSS spinner). Upon completion of the action, then the spinner-id would be presented to the model, say to a "turnOffSpinner" property value. The State function would then remove the corresponding div from the view.
I would try to avoid at all cost to many "loops" in the unidirectional dataflow.
Jean-Jacques Dubray
@jdubray
May 28 2016 20:14
I'll try to build some sample code today. This is a very important question (how to handle some (small) long running state, such as a spinner, in a view component).
Another strategy would be to use another method to the model that the action would invoke prior to triggering the request. Let's call it "stash":
From the view component:
callThisLongRunningAction({spinner: 'my-spinner-id', data: '1234'}) ;
Jean-Jacques Dubray
@jdubray
May 28 2016 20:20
in the action:
callThisLongRunningAction = (data) = { 
      model.stash( {spinner: data.spinner} ) 
      fetch( query, (resp) => {
           ...
           model.present(results) 
     } 
}
That way the model can be in the position to do some clean up in case fetch timesout or something like that
@antitoxic what would be your preferred way of solving it?