These are chat archives for jdubray/sam

1st
Nov 2016
Edward Mulraney
@edmulraney
Nov 01 2016 12:16
is action "hang back" not the same thing as cancelling? (havent looked at his code yet)
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 14:03
Sorry, just catching up. There is no difference between "slow" and async results, an API will return a response which will be used to create the proposal.
Cancelling is just a particular case of "hang back". That's perhaps the most common case. The general way to think about it is "ignore proposals". In the case of a complex UI, such as Facebook/Netflix, you could actually trigger a bunch of "actions" and then you have a rule that says "enough" we no longer accept proposals from that step. It could be we ignore anyproposal after the first one is received, or we ignore any proposal after a specific event (keypressed, scrolldown,...) or we ignore any proposal from that step after some time (10s for instance).
wrt node-uuid, I was probably considering using it for the action instance IDs but then I used another a step counter and and a simple numbering mechanism because I needed to extract the current step of the action instance.
Edward Mulraney
@edmulraney
Nov 01 2016 14:55
all those examples are cancellation/rejection. is there a technological difference between your phrase "hang back" and "cancellation/rejection"
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 14:57
As I said cancellation/rejection is just a particular case, you could still do something useful with the proposal (cache it for instance, at the action level, so next time the same action is called, then the data is readily available). This should be used to optimize scenarios where the users can potentially change their mind quickly, back and forth.
I feel that's a rather uncharted territory because all the other approaches (Elm/Redux) focus on passing the effects through the model before calling an API while here we get the opportunity to do really smart things before the model is involved.
Fred Daoud
@foxdonut
Nov 01 2016 15:28
we no longer accept proposals from that step
So what defines the boundaries of a "step"? I thought a "step" was the propose - accept - learn loop..
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 15:30
Yes, that's the default boundary, that does not change. Here are really talking (conceptually) of a case where an action triggered at a previous step could present its proposal to the model.
The notion of a step is really to say that when you present a proposal to the model, you are actually in compliance with the underlying state machine. It's not "random", or worse, the action making assignments to the model directly.
Fred Daoud
@foxdonut
Nov 01 2016 15:33
So, at the time of creating the proposal (not yet presenting it to the model), the proposal is tagged with a number (1). Time passes before that proposal (P1) is presented to the model. In the meantime, another proposal (P2) is presented to the model and accepted. The rule is, only proposals with a higher number than the last accepted proposal are allowed, so P1 is rejected. Is that correct?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 15:36
What I am talking about is scenarios where lots of things can happen, lots of actions are triggered and proposals are expected. You don't want to pollute your model to keep track of everything that is in flight. You can be smart about when an action is ready to present or even accept a proposal on a different step as long as you know why you do it, it's not random.
Fred Daoud
@foxdonut
Nov 01 2016 15:50
That's interesting. I understand the concept in theory, but am having trouble thinking of a concrete use case, a problem that this solves.
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 15:57
One of the problem you can solve is a complex UI that needs lots of call to be populated (say each time the user does something). They would not fit well as a "single" action, they are truly independent actions with individual proposals, triggered by a single event. That helps you reason about the whole system without the assistance of too much state in the model. The state is the "step" combined with "allowed" actions.
That's what SAFE brings you.
Fred Daoud
@foxdonut
Nov 01 2016 16:06
Hmm, that's a nice idea. Thank you for the explanation.
Edward Mulraney
@edmulraney
Nov 01 2016 16:20
what's the answer to this: "So, at the time of creating the proposal (not yet presenting it to the model), the proposal is tagged with a number (1). Time passes before that proposal (P1) is presented to the model. In the meantime, another proposal (P2) is presented to the model and accepted. The rule is, only proposals with a higher number than the last accepted proposal are allowed, so P1 is rejected. Is that correct?"
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 16:35
yes, this is correct, this is very much in line with Paxos and the way Dr. Lamport helped defined "time" in distributed systems
However, you can go one step beyond and repurpose a past action into a new proposal in subsequent steps. That would be hard to manage from within the model.
It requires of course that you know why you can do it, it cannot be done with any type of actions.
Zach Dahl
@schtauffen
Nov 01 2016 17:15
I'm writing a micro-library which tackles async a little bit differently (by "polluting the model" :P) and I don't know if it is exactly SAM compliant but once I get it finished and get an example put together I'll post it here to compare with the SAFE methodology. The other way I was considering doing it was having async run its very own SAM-loop and communicate between your app's SAM loop and the async SAM loop via actions. They end up being similar approaches when it comes down to it I suppose.
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 17:41
Yes, theoretically (and practically) you can have a SAM instance each time you have some application state and then communicate between SAM instance (either trigger an action or directly present a proposal)
Rick Medina
@rickmed
Nov 01 2016 18:28
is it fair to say that what the present function does is to receive messages from the actions (and its payload), interprets them to move forward the model/state?
and then passes the new model to render
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 18:28
@rickmed yes, exactly
It is the present method which controls advancing step by step
Triggering a function does not necessarily advance the step (an action could decide not to send a proposal)
Rick Medina
@rickmed
Nov 01 2016 18:32
what function do you mean? what would be an example of an action not wanting to send a proposal?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 18:32
Sorry, triggering an action
Say you try to set the volume of a player beyond an agreed level
This is not "an error"
The action may decide to do nothing
Rick Medina
@rickmed
Nov 01 2016 18:33
so I think I (finally :sweat_smile: ) get SAM's general idea, I'm a bit confused about nap, what is it? I'm looking the launcher simple example but I can't find it
ok I get it: so another eg: throttling clicks?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 18:33
You have to think of nap in terms of "automatic actions"
yes, throttling clicks would amount to that too
Rick Medina
@rickmed
Nov 01 2016 18:34
automatic actions?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 18:35
nap stands for "next-action-predicate", once the application state has mutate, there is sometimes the need to trigger an automatic action (for instance in the fishing game, you count the fished caught in the net after a mouseup)
automatic action means that they are not triggered by the user, they are triggered because you reached a particular application state
Other frameworks don't have a good place for that, you can't put it in the view (the view does not know the extend of the model, it only has access to its props)
It is in the rocket launcher:
state.nextAction = function (model) {
    if (state.counting(model)) {
        if (model.counter>0) {
            actions.decrement({counter: model.counter},model.present) ;
        }

        if (model.counter === 0) {
            actions.launch({},model.present) ;
        }
    }
}
Rick Medina
@rickmed
Nov 01 2016 18:39
yes, I'm looking at it.
so nap is like seeing if the system is in a "temporal state" (ie: needs to do aditional stuff, ie, doing actions) and then does it
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 18:41
yes, I am not familiar with it, but that avoids having the first action know about what happens in nap()
Rick Medina
@rickmed
Nov 01 2016 18:42
"temporal state" is not a formal concept :) just trying to understand the concepts myself
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 18:42
If you can it's better to keep the elements of the pattern as decoupled from each other as possible
Rick Medina
@rickmed
Nov 01 2016 18:48
present() triggers both render() and nap(), correct?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 18:49
nap is triggered by the State function. The model requests the state function to render.
Rick Medina
@rickmed
Nov 01 2016 18:53
in the launcher example, nap is triggered by the render function
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 18:55
yes, in the state function (render)
state.render = function(model) {
    state.representation(model)
    state.nextAction(model) ;
}
Rick Medina
@rickmed
Nov 01 2016 18:56
its just that state in that example has a few methods so I wasn't sure which mapped to which concept :sweat_smile:
but I get the overall flow :smile: , now to think the WHYs (the implications :) )
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:00
Note that it's a bit harder to use nap on the server because an action results in a proposal that will render a new state (with possibly other actions triggered in nap())
Of course, you have to make sure that you don't create an infinite loop, so after an action is triggered by nap, you have to make sure it won't be triggered again.
Rick Medina
@rickmed
Nov 01 2016 19:02
well that should be checked by nap no?
by checking the state of the model
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:03
well, it's hard to do ... since nap is triggered by a particular state, so it has no idea whether you intended to remain in that state or not.
Rick Medina
@rickmed
Nov 01 2016 19:05
I'm confused, I thought nap is always triggered by present
or "state"
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:05
sorry, I meant a nap action is always triggered because you are in a particular state
Rick Medina
@rickmed
Nov 01 2016 19:06
Of course, you have to make sure that you don't create an infinite loop, so after an action is triggered by nap, you have to make sure it won't be triggered again. do you mean in the server?
^do you mean in the server?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:07
it can happen anywhere because triggering an action in nap means simply being in a particular state
Rick Medina
@rickmed
Nov 01 2016 19:09
that is what I mean that it should be nap's responsability to check whether a state is correct to trigger an action (as to not do an infinite loop)
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:09
it cannot, it is stateless, all the state is in the model, only the model can update the state
Rick Medina
@rickmed
Nov 01 2016 19:10
if this "if(model.counter>0)" wasn't in the launcher nap function, it would create and infinite loop
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:11
nap must check the state to trigger an action, but if the action does not modify the state, you create an infinite loop
Rick Medina
@rickmed
Nov 01 2016 19:11
absolutely!
that is a good point, do you reckon is a fairly easy foot gun to create?
about infinite loops in sam
there is no other state keeping (stateful) other than the global atom in sam correct?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:16
yes, everything must be in the model, single state tree. The only state that SAFE keeps is the "allowed actions" which are computed by the State function
Rick Medina
@rickmed
Nov 01 2016 19:20
when you say the state function, what do you mean? for example, in the launcher example there is state.render/representation/nextAction/etc
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:21
render
just want to emphasize that's a pure function
Rick Medina
@rickmed
Nov 01 2016 19:22
render is a pure function?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:24
yes S(model)
Rick Medina
@rickmed
Nov 01 2016 19:27
state.render = function(model) {
state.representation(model)
state.nextAction(model) ;
}
what does it return?
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 19:29
ah ... good question, it returns nothing since it is part of the reactive loop, the way to look at it is that it returns the state representation but as part of the reactive loop wiring, it calls display on the view, passing the state representation.
Rick Medina
@rickmed
Nov 01 2016 19:42
I was looking at the API example posted in the chat but couldn't find it... I'm trying know to understand the async workflows..
Zach Dahl
@schtauffen
Nov 01 2016 19:50

async at the simplest could work like:

function myAsyncAction () {
  model.present({ loading: true }) // if you want to show a spinner or something
  asyncSomething(...)
    .then(unwrapResponse)
    .then(model.present)
    .catch((e) => model.present({ loading: false, error: e }))
}

It becomes more complicated when you want to be able to cancel things, such as long-running or expensive gets.
If there is little downfall to simply ignoring an async model.present(...) when it is no longer applicable then that is the preferred course.

Rick Medina
@rickmed
Nov 01 2016 20:20
@schtauffen got it. thank you. Yes, that case is necessary as well. How would it be implemented? (eg: a drag and drop)
Jean-Jacques Dubray
@metapgmr_twitter
Nov 01 2016 22:36
@schtauffen I like it!
It's perfectly legitimate to present twice rather than implementing the async call in nap()
Fred Daoud
@foxdonut
Nov 01 2016 23:42
Fred Daoud
@foxdonut
Nov 01 2016 23:52
Glad you approve @metapgmr_twitter