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
    so you can add healthpoint along the way
    The idea is that the model has the knowledge to update the application state
    the action has the knowledge to validate events and do some calculation to present values that the model would understand
    the view should have no knowledge of the model
    Actions are like mini adapters between the view and the model
    the view generate an event, the action translate that event into proposed values to mutate the model
    but still the action does not know if the update will succeed
    other conditions might influence the outcome of the intent
    Daniel Neveux
    @dagatsoin
    about the other concitions:
    in my case, a player could have a spell (a debuff) on it which will be decrease the received heal point +3 . (the spell is a property of the model)
    During the present computation, if the model detects some healpoints in the data set, il will minus it of 3.
    As the action does not have to know if the player has a debuff, is it correct that this computation is done in the model?
    Jean-Jacques Dubray
    @jdubray
    exactly, the action / intent should not know about that.
    Daniel Neveux
    @dagatsoin
    (for what it is worth the "pipeline" image is crystal clear for me, in fact it reminds me the way Redux pipes the state computation)
    Jean-Jacques Dubray
    @jdubray
    It's not an absolute rule, it is just about separating the "proposal" from the "acceptance" which I believe has a lot of value.
    Yes, except that Redux does not separate the proposal from the acceptance
    that's one of the two main issue with it
    the second issue is the use of Saga which break its principle: single state tree
    Daniel Neveux
    @dagatsoin
    yes, it is why I switched to sam
    Jean-Jacques Dubray
    @jdubray
    nap() is a lot simpler to use than Sagas.
    Sagas have a "memory" effect that is very detrimental to reason about your code
    it works in a small scale, but will be very hard to reason if you start having many sagas running concurrently (it's actually "waiting" concurrently).
    Worse, you could end up putting everything in a single saga
    SAM is very close to redux, but I feel it is different enough and offer a more natural factoring, not to mention the overall complexity of the React/Redux stack that starts getting out of control.
    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.