These are chat archives for jdubray/sam

24th
May 2016
David Fall
@509dave16
May 24 2016 14:10
A couple of weeks ago I finished a SAM implementation of a Tic Tac Toe web app using React, Redux, and Firebase. I would appreciate any feedback people would be willing to give: https://github.com/509dave16/sam-tic-tac-toe. The only responsibility of the reducers is assigment while all of the business logic and side effects happen in the Actions.
Jean-Jacques Dubray
@jdubray
May 24 2016 14:32
fantastic! I'll take a look this morning.
thank you!
David Fall
@509dave16
May 24 2016 14:53
@jdubray Thanks! If you have any questions let me know. I tried to write my code to be as self-documenting as possible, but that can only goes so far.
devin ivy
@devinivy
May 24 2016 15:02
@509dave16 was it natural/enjoyable developing with SAM?
David Fall
@509dave16
May 24 2016 15:23
@devinivy There's definitelly a pattern to how you have to approach writing the code. Usually went along the lines of define initial Model properties, write State function, write View/NAP actions for that State, add present guard logic for presented datasets/payloads, and then write State Representation snippet. Once I got that down it wasn't too bad, but you do have to plan things out well. Changes to the Model are going to cause cascading changes throughout your application I had to spend some time devising ways to make setting up reducers as simple as possible, since most of the time all that was needed wasn't a simple assignment. I used the package 'redux-actions' to help with that so that I didn't have to write any switch statements. Sometimes you encounter infinite loops because either the State was setup incorrectly or the Model wasn't mutated properly. But those aren't too hard to debug since you can follow the unidirectional flow of SAM. Using the SAM pattern made it easy to transition everything but the State Representation(View) to React Native. I still need to figure out the best way to write a package, so that I'm not copying and pasting the SAM implementation between a React and React Native application.
David Fall
@509dave16
May 24 2016 15:35
*was a simple assignment.
David Fall
@509dave16
May 24 2016 15:43
@devinivy In terms of it feeling natural, I think it became more natural over time as I became more comfortable with the pattern. It terms of enjoyability it's probably going to depend. For my simple Tic Tac Toe application, it took quite a bit more code to complete using the SAM pattern in comparison to my initial implementation: https://github.com/509dave16/tic-tac-toe-react-firebase/ . But I feel that my code is more easy to reason about, understand, and enhance to some degree.
Fred Daoud
@foxdonut
May 24 2016 17:06
@509dave16 what did your initial implementation use?
Jean-Jacques Dubray
@jdubray
May 24 2016 17:06

This is definitely the way to go

The only responsibility of the reducers is assignment while all of the business logic and side effects happen in the Actions.

David Fall
@509dave16
May 24 2016 17:07
@foxdonut Still used React and Firebase. No Flux library though. And no particular pattern was used.
Fred Daoud
@foxdonut
May 24 2016 17:16
@509dave16 so would you say it was "easier", but the code to manage the data flow was scattered all over the place? kind of like using jQuery vs something more organized?
James Wilson
@jsdw
May 24 2016 17:22

Hello! I have just been reading about SAM for the first time and I'm intrigued. It strikes me as being very similar to The Elm Architecture (see eg the example http://guide.elm-lang.org/architecture/effects/http.html), but there might be some distinction w.r.t how actions are handled from what I gather.

In elm, actions are just data, and they are fed to the update function (along with the current model), which is responsible for providing back a new model and optionally effects that need running (which could eg get json, decode it and prepare it for the update function). The result of those effects is fed back into the update function again.

In SAM, I gather that actions encapsulate all of the logic of performing effects and preparing the data for the model, and the update function (or urm, model.present in some SAM examples) just takes the finished data. I can't decide whether this is identical to Elm or whether there is an important difference?

David Fall
@509dave16
May 24 2016 17:23
@foxdonut I would say that it was "easier" to not follow a particular pattern when implementing it the first time. And the mutations to the Model were scattered throughout different event handlers. You could compare it to JQuery where all the event handlers are going to be mutating the Model directly.
David Fall
@509dave16
May 24 2016 17:32
@jsdw What you are describing concerning Elm does sound similar to SAM. I can't say whether are not they differ or are identical, as I have never used Elm. @jdubray would probably have a better answer to give when he is able I imagine.
Jean-Jacques Dubray
@jdubray
May 24 2016 17:35

@509dave16 Here are a few preliminary comments:

Model: Holds the Application Model

Model holds the application state as a set of property values

Next Action Predicates are triggered based on the Control State and should execute an Action.

there is only nap function and its argument is the model, we can also pass the "control state" such that it is not computed twice, but I am trying to convince people to not use "states" from state machine. They should rather use if-then-else

State Representation: Given Model Values and Actions based on the Control State

the actions are out of the picture here, the state representation is solely computed based on the model property values (and corresponding control state if it helps derive the correct view)

Jean-Jacques Dubray
@jdubray
May 24 2016 17:45

render: A function called by the Model's 'present' function after mutations are done that should in turn pass along the Model Values to the State Representation

render is called on "State" it is from there that you would compute the state representation and invoke nap

David Fall
@509dave16
May 24 2016 17:47
@jdubray Yeah. Those comments/thoughts I had on understanding SAM were mainly for my benefit as I tried to understand how all the pieces fit together. I probably should just take that section out of the README so as to not confuse people. I will need to think on what you have said.
Jean-Jacques Dubray
@jdubray
May 24 2016 17:49
At first glance you seem to be using nap() as a dispatcher. In general there is not much going on in nap, very few applications have an "automatic" action that needs to be triggered once the state has mutated. It happens often enough to have it, but it should be use only for automatic actions.
Redux uses Sagas, the problem with them is they are stateful, nap() is a pure function of the model.
@jsdw welcome! Actually it's quite different from Elm as I understand it. When I look at this presentation there are quite a few differences.
James Wilson
@jsdw
May 24 2016 17:51
@509dave16 thanks! I am hoping there are strong similarities because I understand Elm, so it would fit well with my mental model of things
Jean-Jacques Dubray
@jdubray
May 24 2016 17:52
SAM's main goal is to completely decouple the view from the model. Both Actions and State achieve that purpose.
Elm, Redux, Cycle.js... all consider that a (user) event is ready to start the application state mutation. SAM says not quite, mutation can be decomposed as a "propose" phase and an "accept" phase.

When you have:

counter = counter + 1

Logically what's happening is

p_counter = counter + 1 
counter = p_counter
James Wilson
@jsdw
May 24 2016 17:54
@jdubray Interesting! So in Elm, the "model" is more a component model; the state that the component is interested in and nothing else, rather than an application level model (eg cached details for things, authentication state etc)
Jean-Jacques Dubray
@jdubray
May 24 2016 17:55
it looks trivial that it has major consequences in your ability to deal with Side-Effects, cancellations (or hang backs as I started to call them)
James Wilson
@jsdw
May 24 2016 17:56
Elm actually does a very similar thing w.r.t automatic actions I think
so
Jean-Jacques Dubray
@jdubray
May 24 2016 17:56
In the propose phase there are no state mutation at all, you just validate and enrich the user event to be in the position to mutate the state. That a healthy decoupling. You don't want to do that in the view, that would be too coupled to the model
In particular it's ok to make an HTTP request to enrich the event because you have not started the mutation, other mutations can actually pass through if it logically makes sense
James Wilson
@jsdw
May 24 2016 17:57

for example, an update function in elm might look like:

update msg model = case msg of
   Increment -> (model + 1, Cmd.none)
   Decrement -> (model - 1, someEffect)

and so the thing that calls update on the components model gets back a new model, but also proposed effects that the model might like carried out (which themselves send their result back into the update function as a massage)

Jean-Jacques Dubray
@jdubray
May 24 2016 17:57
So the pattern remains non blocking, it simply keeps track of "steps" which are made up of proposals, acceptance and "learn"
James Wilson
@jsdw
May 24 2016 17:58
and so the ability of the update funciton in an elm component to return effects as well as a new model state allows for automatic actions
(the model's init function looks very similar, so the component can ask for effects to be performed when it is first created as well)
Jean-Jacques Dubray
@jdubray
May 24 2016 17:59
Now, in the model you can also call the persistence layer because sometimes that's logically what needs to happen: no other action can present data until the model has mutated and it might need the result of the persistence layer (say a new record identifier) before the system can logically proceed.
James Wilson
@jsdw
May 24 2016 17:59
catches up on reading
Jean-Jacques Dubray
@jdubray
May 24 2016 18:00
Yes, unlike Redux, Elm's updates, I agree, would work as nap(), however, most of the "updates" in SAM operates within the present method of the model, they are invisible to the rest of the system.
It is the State function (render) that is in charge of informing all the parts of the systems of the new application state.
That gains helps with decoupling because the model doesn't know anything about who might want to know of the changes
nap does not do "updates" per se, all the mutation is in the model.
Please not that in SAM, nap() triggers an (automatic) action that will in turn present data to the model and so on.
The key is really to understand that there is a flow to mutation: propose, accept, learn (see Paxos protocol)
Jean-Jacques Dubray
@jdubray
May 24 2016 18:05
My issue with Elm, Redux, Cycle... is that they generally do not account for that flow, they cut right through it, when they think it is ok. Sometimes it's ok, sometimes it's not. Yes you can find short cuts, but they will be arbitrary and become very hard to reason about.
By encapsulating all the mutations in the model, SAM logic clearly separates what you need to know to present new values to the model (you need to know very little if anything), so actions can be very reusable across applications. Think about a "change of address" actions that present a new user address to the model. You could easily pack an API to get the corresponding postal address. Why would you present some dirty value to the model? How reusable would be that action?
With SAM you can even have 3rd party actions weaved into your application (using OAuth) for instance.
Now when I look at the example that you provided, I see some unwanted coupling:
update msg model = case msg of
   Increment -> (model + 1, Cmd.none)
   Decrement -> (model - 1, someEffect)

why someEffect would be tied to a "Decrement" event?

nap() derives the (control) state (as in the control state of a state machine) and literally decides what needs to happen next, it does not matter how the model got there (and that's a very important semantic).

@HighOnDrive didn't like my "wires of Bangkok" example, but this is truly what happens when you wire events and handlers without this clear delineation: propose, accept, learn
Jean-Jacques Dubray
@jdubray
May 24 2016 18:11
When I look at the code you provided, I see a direct line of sight between the events and the effects, and that's what SAM is trying to break. I believe that model is wrong, or at least not general enough to enable us to untangle modern Front-End architectures.
In SAM, the model does not know (too) much about the event (I use a dataset as the argument to the present method, not an action/event)
The State function doesn't know (much) about the mutations of the model, it just knows what to do with property values
@509dave16 I am about to board a long flight so I'll work on your code then. I think they even have wifi!
David Fall
@509dave16
May 24 2016 18:17
@jdubray Comment Concerning NAP: Instead of creating State functions that indicated an automatic action should be triggered, I could have moved the functions somewhere else in the code that was more appropriate. I also prefer to use loops when possible over if/else . And the reason I have so many automatic actions is because there's the element of playing either a "local" game in the browser or an "online" game. The Actions that are triggered by the User should not need to know whether or not the user is playing a local or online game. Instead the appropriate automatic actions are triggered based on the game type(local/online).
@jdubray Thanks for taking the time to look at my code!!! I appreciate the feedback.
Jean-Jacques Dubray
@jdubray
May 24 2016 18:20
I'll take a look I have to go board my flight now.
@509dave16 you've done a lot of work, that's the least I can do
David Fall
@509dave16
May 24 2016 18:49
@weepy I completed that SAM implmentation of Tic Tac Toe you were wanting to see: https://github.com/509dave16/sam-tic-tac-toe
James Wilson
@jsdw
May 24 2016 19:26

@jdubray thanks for running through that! So as I understand it then, an improvement to the above sample I gave might be (still completely possible and valid in Elm):

updateModel msg model = case msg of
   Increment -> model + 1
   Decrement -> model - 1

makeActions model =
  if model == 1 then someEffect else Cmd.none

-- the same signature as my last example so it wires
-- into the elm architecture exactly as it would have before:
update model =
  let newModel = updateModel model
  in (newModel, makeActions newModel)

Here, the figuring out of what effects to perform is totally decoupled from the updating of the model and based only on its state

Fred Daoud
@foxdonut
May 24 2016 20:24
hi @jsdw ! having looked at both Elm (TEA) and SAM, I'd say generally TEA is more similar to Redux than SAM. The main difference with SAM (as I understand it) is that it is more flexible with the data flow, namely, that the model.present function decides to accept or refuse an update, making it easier to cancel an action or to prevent inconsistent states. Actions are more flexible in SAM, they could just be plain data, or closer to Elm/Redux actions (named actions that carry a payload.) Where side effects happen is also more flexible in SAM, they could be part of actions, or within the model object itself.
Finally NAP() makes it easier to implement "after action A, action B should automatically trigger"
Jean-Jacques Dubray
@jdubray
May 24 2016 20:28
@foxdonut thank you, that's a really good summary, I'd agree there are the main differences.
Fred Daoud
@foxdonut
May 24 2016 20:32
happy to hear it @jdubray
I am working on a small library that is (hopefully) compatible with SAM
Jean-Jacques Dubray
@jdubray
May 24 2016 20:36

Happy to hear!

The problem I see with Elm and Redux is once an action is dispatched it has to hit the reducer. With SAM you could easily trigger several actions in parallel and then have rules that decide that the first one wins. You can also suppress actions if it makes sense. The bottom line is that there is little value keep track of things that are in progress say like "fetching", IMHO it is a healthier approach to separate propose from accept. When you start factoring Sagas which maintain their own states things start to get very complicated.

Fred Daoud
@foxdonut
May 24 2016 21:18
Having to pile on features and tools to achieve the desired results perhaps are telling of a flawed architecture. Similarly to a new programming language that looks good initially, but then has to pile on tricks and inconsistencies when its shortcomings are revealed.
I think it is telling that you can't achieve SAM with Redux, but you could, technically, achieve Redux with SAM.
Jean-Jacques Dubray
@jdubray
May 24 2016 21:27

At a minimum, they ought to look at it with a critical eye. I am not claiming that SAM solves everything, originally my intent was really to start a discussion with Dan and ask him if he had considered alternate semantics.

Today, you go to the Redux's page and it seems there is a library for every question you have: redux-sagas, redux-gen, redux-loop, redux-effects, redux-side-effects, redux-thunks, rx-redux, redux-rx (and that's just a few). That's not very cool.

The other thing is that Redux has to compose with the way React works, i.e. as soon as you mutate the state, then Redux tries to render. With SAM, the state function decides when and how to render. Automatic rendering looks great on paper, but it is very constraining in a single state tree.

devin ivy
@devinivy
May 24 2016 21:29
and immutable state doesn't play nice with every view layer
i use polymer, and polymer sort of relies on being able to describe the state's mutation.
at least for performant renders
you describe the state change, and it lifts that (directly) to DOM changes
Jean-Jacques Dubray
@jdubray
May 24 2016 21:43
on average you have a good idea of what needs to be rendered, it sounds a bit like overkill to have to be a complete diff. The other issue with immutable state is that you have to create a new copy for each dispatch. That can quickly run into scalability issues if your application state is large.
Fred Daoud
@foxdonut
May 24 2016 21:46
excellent points
James Wilson
@jsdw
May 24 2016 22:15

@foxdonut @jdubray intersting points, although I'm not sure I completely agree w.r.t Elm. You say that the model.present function (which is analogous to elm's "update" function in my code snippets above) can choose to accept or reject an update for instance, and that the update can have a payload or not; all of this is perfectly true of Elm's update function as well; it can decide whether or not to act based on the update and the current model. Or have I missed the point?

In Elm, messages to the update function can come from anywhere, for instance the component could subscribe to receive websocket events, and ask for them to be passed in as messages (after some processing if you like), or messages can come from user interaction with the view. Since the update function can trigger side effecting actions (talking to a backend, all that sort of thing), side effects can be triggered by anything that triggers an update (so, automatically in response to the model state, or manually in response to user interaction, albeit less direct).

I quite like the fact that everything you'd want to do to impact a component has to be done via its update function - I have one place to look if the component doesn't work how I expect, and I can manually pass messages in and see how the state changes to test the component model in isolation of effects. I'd assume much of this is true of SAM as well! I am struggling I think to see the concrete benefits of each approach compared with eachother and may just need to experiment (although I'm not certain I have a good enough grasp of what SAM is yet)! It may also be that SAM is more general whereas The Elm Architecture is more focused on frontend UI and less applicable in other cases :)

Fred Daoud
@foxdonut
May 24 2016 23:02
@jsdw as far as this:
it can decide whether or not to act based on the update and the current model. Or have I missed the point?
The difference is not huge, but what it is, is that the update function has to return something, and the reducing loop/signal emit/view update cycle still triggers.. In SAM, refusing an update amounts to doing nothing at all.
As for actions being just data vs. being typed and having a payload: my understanding is that [idiomatic] Elm is the latter only.
Fred Daoud
@foxdonut
May 24 2016 23:09
I'm not saying it's a bad thing; this is probably reaching more into the differences between Elm as a language vs JavaScript; Elm is definitely very good at what it's good at, in that respect.
Jean-Jacques Dubray
@jdubray
May 24 2016 23:53
@jsdw @foxdonut would you say that's an accurate rendering of ELM? http://www.elm-tutorial.org/en/02-elm-arch/04-flow.html
blob