Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    Jean-Jacques Dubray
    @jdubray
    SAM has no memory effect, it does not care how you got to a particular application state, just like any state machine, but without the unnecessary burden of state machine semantics.
    Actually, once you understand TLA+ in the context of state machines you realize that these semantics are not general enough. It is not the action that decides in which state you end up, it is truly a propose/accept protocol, and it is only once you accepted the proposed value that you can decide in which state you ended up. It is from to think that you would know the end state from the action you invoke, it's true in certain cases, but way too strong of a constraint when you are designing real world systems.
    Daniel Neveux
    @dagatsoin
    About that. As you said, actions sequence could be valid if I am sure that it synchronously executed. So, to get an alternative to the pipe example. Let's say that my action are asyncrhonous, it a big deal to use generator yield/next to sequence the action? It seems to me:
    1- write less code
    2- reserve nap() to important function
    Jean-Jacques Dubray
    @jdubray
    Yes, as long as you follow the propose/accept/(learn)/propose/accept/... sequence you are fine. It does not matter how this is achieved.
    Daniel Neveux
    @dagatsoin
    ok great
    Jean-Jacques Dubray
    @jdubray
    But really logically, it seems that you are talking about an automatic action. But again, I don't think it is necessary to be prescriptive as long as:
    1. the propose/accept flow is followed
    2. present method is the only one that can mutate state
    3. present can only process one action at a time
    You just have to make sure the state of the saga is not relevant to the application state
    Otherwise, you break the principle of having a single (application) state tree
    Daniel Neveux
    @dagatsoin
    and the pure action principle
    Jean-Jacques Dubray
    @jdubray
    well, it's pure in the sense that it does not mutate the application state. Perso, I feel it's ok for the actions to have side-effects (like make a query to enrich/validate the intent)
    Daniel Neveux
    @dagatsoin
    yep
    Jean-Jacques Dubray
    @jdubray
    SAM-SAFE would "block" any further action to be triggered, until the action flow has completed (unless you use action hang back/cancellation)
    That's the main message behind SAM, it does not matter how long propose/accept/learn takes, it could take 5 days, if it made sense
    Daniel Neveux
    @dagatsoin
    even if the action triggers other actions (like in my case?)
    Jean-Jacques Dubray
    @jdubray
    well, again, as long as you follow the flow, it does not really matter how these actions are triggered (users, external agents, sagas...)
    what is more important is for the acceptor (the model) to always be in the position to resolve the question whether they can accept a dataset or not
    If you follow a traditional state machine semantics that kind of code becomes untractable
    Daniel Neveux
    @dagatsoin
    I will get inspired from sam-sage to write my action "guard".
    Jean-Jacques Dubray
    @jdubray
    happy if it can help
    Daniel Neveux
    @dagatsoin
    got to go Jean Jacques. Thx so much for the discussion :+1:
    Jean-Jacques Dubray
    @jdubray
    that's the other big thing that seems to be missing in Redux, they don't have a good way to deal with these authorizations outside the reducer. IMHO, it's too late once you reach the reducer.
    Thank you !!
    Nivani
    @Nivani

    @jdubray I read the article on SAM pattern on infoq and the website http://sam.js.org/. It was interesting reading, but I also had some questions, so I'm creating a simple application in typescript using SAM pattern: https://github.com/Nivani/todo-sam-incremental-dom

    I disliked how if statements determine how to mutate the model in the model.CRUD() function found in this example: https://github.com/jdubray/sam-samples/tree/master/todomvc-app
    I am currently using the visitor pattern to try and clean it up a bit (you can see it in my example in ts/model/model.ts), but there's still a lot of boiler plate code imo. I keep wondering why actions should present data to the model in the form of an object instead of just calling model functions that will mutate the model.

    I am thinking of implementing the interaction between actions and the model differently. In the following code, "mutations" is an object containing only functions that can be called to update the model. "present" might no longer be the best name here, but I kept it to make it clear that this would replace the present method.

    model.present = mutateModel => {
        mutateModel(model.mutations);
        state.render(model) ;
    };

    actions would then be implemented like this:

    actions.selectItem = selectItemId => {
        model.present(mutations => mutations.selectItem(selectItemId));
    };

    What do you think of that, is that something that undermines some part of the pattern?

    Jean-Jacques Dubray
    @jdubray

    I disliked how if statements determine how to mutate the model

    It's all right, no worries

    why actions should present data to the model in the form of an object instead of just calling model functions that will mutate the model.

    That's kind of the whole point of the pattern, all these "if" statements represent a unit of work. If you use setters to set the values from the actions, you cannot be guaranteed that the model will always be in a consistent state. It works like a critical section, processing one action at a time. When the mutation completes, the State function is also in the position to decide which actions are allowed.

    Jean-Jacques Dubray
    @jdubray
    As I mentioned in the paper there are several styles that could be used. Ideally I suggest you keep a many-actions-to-one unit-of-work relationship. The reason for that is several actions could have app specific logic that all invoke the same unit of work once they compute the mutations. I chose datasets over labels because I wanted the actions to know as little as possible from the model, but I hear your point.
    Jean-Jacques Dubray
    @jdubray
    Do you know about the "union-type" library? https://github.com/paldepind/union-type
    I believe this is close to what you are trying to do with the visitor pattern
    @Nivani take a look at the section on "switching on union types"
    const advancePlayer = (action, player) =>
      Action.case({
        Up: () => ({x: player.x, y: player.y - 1}),
        Right: () => ({x: player.x + 1, y: player.y}),
        Down: () => ({x: player.x, y: player.y + 1}),
        Left: () => ({x: player.x - 1, y: player.y}),
        _: () => player,
      }, action);
    Nivani
    @Nivani
    @jdubray I understand what you're saying about units of work and critical sections. In my example the mutations object would contain functions that handle units of work, not setters for setting model values. That way the model would remain in a consistent state and a many-actions-to-one-unit-of-work relationships would not require extra effort.
    I have not seen that library, but it looks like something that could be used here. It makes me think of pattern matching in functional languages. Thanks for the information, I will have a look.
    Jean-Jacques Dubray
    @jdubray
    yes, I think we are in synch!
    happy to help anytime if I can and don't mind the critics, makes all of us better.
    Slađan Ristić
    @sladiri

    Hi, I am wondering how partial updates and broadcasts would fit in. Say I have an increment action and the model is updated. Can the state-function invoke an automatic refresh action on behalf of all connected clients when the action was increment? That way all clients would get the new state of the model, and I would avoid polling.

    part2: That would also be an incremental update then and the state-representation would update partially only.

    Jean-Jacques Dubray
    @jdubray
    @sladiri I often talk about making a delineation between programming model, wiring and architecture. SAM is only a programming model pattern, it makes no assumption as to how the pattern is wired. When you look at the State function, it does three things:
    • compute the state representation
    • trigger the refresh of the state representation
    • trigger the next-action predicate
    Jean-Jacques Dubray
    @jdubray
    For instance in the angular2 samples you can see I use pub/sub, so in this case, the State function publishes the new state representation and any subscriber would get notifier. If the state function was running on the server you would need something like webSocket to propagate the changes, but again all the wiring is independent of the pattern.
    Slađan Ristić
    @sladiri
    Yes, I also try to use a message bus to connect actions, model and state. So I guess if the model set a field broadcast or no-op, the state function could trigger next-actions optimise by not returning anything in case of a no-op? I have to get used to what model means, I think :smile:
    Slađan Ristić
    @sladiri
    I mean that is how I understand the second thing in your example:
    trigger the refresh of the state representation
    I was not sure, if that refresh was optional.
    Thank you JJ!
    Jean-Jacques Dubray
    @jdubray
    @sladiri exactly, the model can give some hints, even keep a "change summary" to optimize the computation and rendering of the State Representation.
    It's really a key value of SAM to separate the Model from the State (representation). It keeps all the representation and propagation concerns out of the model, it also keeps all the decision process around next-action separate from the model.
    Of course it would also allow you to use different State function on top of the same model. Nothing says you should only have one!
    The refresh is just a special "next-action", that's why it is separate. You could also handle all the rendering within the next-action predicate. I prefer keeping them separate, but that's just personal taste.
    You could also decide if there is a next action, or depending on type of next action you will skip rendering, in that case the order in which they happen is important. I can't find a general solution for it, not sure it exists because it really depends.
    You are welcome! any time!
    Slađan Ristić
    @sladiri
    1. I guess the hints are "polluting" the model but might be required in reality. Currently I am passing through the action data, and the state function can derive the hint from that.

    2. In my case it was the easiest to separate the automatic refresh action into another state function indeed. Actually, I have one state function, which is also composed of two other state functions now it seems. The first handles a client session, the second handles broadcasts, the third would be the "typical" state function from your examples. :smile: I can probably decompose the main into another one to decide rendering separately, yes.

    These are just my experiments for school, I like how it turned out however.

    Jean-Jacques Dubray
    @jdubray
    1. agreed, the alternative is vdom, the only one I tried so far with React. I wrote a lot of code without vdom and it seems fine in a lot of cases. I am just careful not to introduce too many dependencies, they are hard to manage over time as upgrades are provided.
    2. Yes, multi-client applications are a specific use case and would require this kind of structure. The good news is that the pattern does not seem to break easily when you throw more complex scenarios at it.
    Daniel Neveux
    @dagatsoin
    Hi @jdubray I have an issue about this line: https://github.com/jdubray/sam-samples/blob/master/vanilla-api-calls/index.html#L56 In my mind, triggering something from the model is like a side effect and that is outside the model purpose. Why don't put this in an action?
    Daniel Neveux
    @dagatsoin
    I get it. "Fire and forget" is fine in the model. https://gitter.im/jdubray/sam?at=58336ad4bc17b2e756fb5888
    Jean-Jacques Dubray
    @jdubray
    either way it is fine
    "Theoretically" you are correct
    in practice:
    1/ it's either fire and forget
    2/ the user may want to wait for the result before updating the view
    Jean-Jacques Dubray
    @jdubray
    so it's a "hack", but that's strictly equivalent