These are chat archives for jdubray/sam

11th
Jan 2018
Jean-Jacques Dubray
@jdubray
Jan 11 2018 09:48
Sorry for going off topic, but this presentation is just too good to pass on, humor and science at its best. Beying a (still) living proof of the power of this information, this is worth your time.
Jean-Jacques Dubray
@jdubray
Jan 11 2018 12:26
I guess this immer video should easily grab the attention to any Redux developer.
Łukasz Lityński
@hex13
Jan 11 2018 13:22
boring :) I've been doing something like this in my Transmutable few months already :)
although it's bugging me that creator of Mobx has a better marketing. I mean, I respect the guy, actually Transmutable was inspired by Mobx, but hello. It's just a library. No need for making fad out of it
but maybe I should make some kind of fad from Transmutable
Jean-Jacques Dubray
@jdubray
Jan 11 2018 14:41
@hex13 clearly I have nothing to teach you here :-)
my concern from a SAM's perspective is that you need to:
  • make a distinction between the proposal computed by the action and the mutation
  • support a many-to-many relationship betwen action and acceptors
  • position API calls (queries and commands) with actions and acceptors
Immer doesn't help with either
Jean-Jacques Dubray
@jdubray
Jan 11 2018 14:47
Redux calls an action what is really an event (or a proposal at best when you use action creators consistently), but the duality doesn't help developers. It would be better to redesign the programming model from scratch now that we know where some of the issues are.
devin ivy
@devinivy
Jan 11 2018 15:52

@jdubray one thing i struggle with in SAM is actions-as-proposals. before you read into that, i do believe that proposals should occur! i wonder if there actually needs to be some more factoring to really nail it. i.e. the opposite side of model --state--> view might be intent --proposal--> model

one way to see this,

support a many-to-many relationship betwen action and acceptors

i fully agree with this. but if an action is a proposal for a specific change to state, does that mean that a proposal must be intended for multiple acceptors? does the action really have to have insight into the entire model? i could imagine a world where intents and proposals are different– a user intent (e.g. DISMISS_NOTIFICATION) may be turned into several proposals for separate acceptors (e.g. { removeNotificationId: 10 } and { pendingRequests: 'increment' }).

devin ivy
@devinivy
Jan 11 2018 18:43
(in practice i find that the model interpreting an action as one or more proposals works fine– the most important thing is for the model to actively keep itself in a valid state)
boris sabadach
@bsabadach
Jan 11 2018 19:11
Hello everyone, this my attempt to build an application with SAM architecture but also with components; comments welcome https://github.com/bsabadach/countries-explorer-vdom-sam
devin ivy
@devinivy
Jan 11 2018 19:13
@bsabadach :clap:
boris sabadach
@bsabadach
Jan 11 2018 19:18
@devinivy thanks! you are running it?
btw it doesn’t work on the fucking new version of Firefox, markers don’t load
devin ivy
@devinivy
Jan 11 2018 19:21
no, but i'm reading through it :)
i like how it's laid out :ok_hand:
Fred Daoud
@foxdonut
Jan 11 2018 19:30
woah, f-bomb, kindly watch the language please :open_mouth:
devin ivy
@devinivy
Jan 11 2018 19:54
weird, my eyes skimmed right past that when i read it
Jean-Jacques Dubray
@jdubray
Jan 11 2018 21:48

@devinivy Sorry, I am on a diferent timezone, just waking up right now.

does that mean that a proposal must be intended for multiple acceptors? does the action really have to have insight into the entire model?

absolutely not, it is the model which interprets the proposal to decide which acceptors will be triggered. Unlike an event which would only relate to what has happened and/or express an intent, an action must actively translate the event/intent into a proposal, so as soon as you enter the action you are in the app logic. At that point the action has a good idea as to what needs to happen to the application state, but only within its context. It has the authority (as part of creating the proposal) to tell the model I want you to accept these values. The model should have no logic that translate event properties into model values. The model should know nothing about external events.

That being said, the model can be quite broad, well beyond the context of an action and that's when multiple acceptors could potentially be triggered. That's the part I don't like at all about Redux (not that it could not be implemented, it would be relatively trivial), but the mechanism that ties an action label to a reducer is way too much coupling. Not to mention that Actions in Redux are exactly events from the view, which again is erroneous. This is not how it works.
I have not found a better way, so far, to implement that decoupling than having the model looking at the structure of the proposal. That's why I found the Monad pattern interesting. Not from a temporal logic perspective, but from a action-acceptor decoupling perspective.
I am on vacation until Monday, I was planning to do more reading and coding, but I got lazy.
Jean-Jacques Dubray
@jdubray
Jan 11 2018 21:56

different– a user intent (e.g. DISMISS_NOTIFICATION) may be turned into several proposals for separate acceptors (e.g. { removeNotificationId: 10 } and { pendingRequests: 'increment' })

I personally would not do that. From a temporal logic perspective, an action ought to create a single proposal. In that case, the proposal would be like:

   { removeNotificationId: 10, pendingRequests: 'increment' }

Remember that the action knows about the model structure but only in its context, it does not know about the scope of the mutations.

If you feel you need more than "one" action it is either because there is a logical, functional, decomposition of the action (multiple events share the same proposal logic) or because some elements of the proposal actually belong to an acceptor. In your example I would argue that pendingRequests: 'increment' is most likely derived from an acceptor, it should not be part of the proposal.
devin ivy
@devinivy
Jan 11 2018 22:00

pendingRequests: 'increment' is most likely derived from an acceptor

the acceptor receives { removeNotificationId: 10 } and decides to increment some requests counter on its own?

Jean-Jacques Dubray
@jdubray
Jan 11 2018 22:02
I am not sure about your scenario, but yes, the idea is that other acceptors would "expand" the proposal to mutate the model. That's how I do it all the time.
For me that aspect is essential because otherwise you tend to couple all these mutations, that's when the spaghetti forms.
... and the app logic becomes untractable.
david kaye
@dfkaye_twitter
Jan 11 2018 22:07
Those looking for more Leslie Lamport mentions can start here (contains many other links, too): Why you should use modeling [with TLA+/PlusCal]
Jean-Jacques Dubray
@jdubray
Jan 11 2018 22:20
I certainly would be the last person to recommend not using TLA+/PlusCal and modeling, but practically, when you work on some complex business logic that involves (data) relationships and control state, it still seems impractical to me. I corner case on one of my project was brought to me yesterday. In that particular part of the code, I am checking for a one-to-many relation (say like Author and books) in a dataset, it turns out that it is possible that the records can have duplicates (on paper it's illogical, because you would not submit the same book twice, but in practice, it happens WTF??). I am sure, PlusCal would have caught it, but what would have been more efficient? modeling or catching defects as they come? If you are working on AWS, I get it. On other projects?
devin ivy
@devinivy
Jan 11 2018 22:22
@jdubray that makes sense to me, but at that point, what's wrong with the proposal being something that looks like a traditional redux action i.e. { type: 'DISMISS_NOTIFICATION', payload: { id: 10 } }? is that better than a proposal like { removeNotificationId: 10 } if the acceptors are going to expand the action to some more proposals?
i guess your point is that the action should be speaking the language of the model, not the other way around. would you say that is accurate?
Jean-Jacques Dubray
@jdubray
Jan 11 2018 22:22
For me what's more important is the maintainability of your project. What I found with SAM is that my code is infinitely easier to reason about and maintain than without SAM. With SAM, I never have a question as to where the code belongs.
devin ivy
@devinivy
Jan 11 2018 22:23
sure– i'm just asking about one aspect of SAM to ensure i understand it
Jean-Jacques Dubray
@jdubray
Jan 11 2018 22:25
@devinivy nothing, I actually label my actions too, with the hope that one day I will need it, so far I never had, ever, to check on the action label. I would not say it's better, it's really a matter of preference, I know people don't like the way I code, it's ok. My style is based on maximizing the use of extensible data structures, while most people prefer (type) safety.
What I see commonly is that my proposals would contain a list of properties (as well as context) and my acceptors tend to pick up a small subset of these properties.
That's why I am hesitent to use an action type. I don't see it as viable or general enough, in practice.
Jean-Jacques Dubray
@jdubray
Jan 11 2018 22:47
@bsabadach small typo to install your simple (gobal -> global)
Jean-Jacques Dubray
@jdubray
Jan 11 2018 23:11
@bsabadach really cool sample. I am parsing the code, and based on the discussion we just had with @devinivy, personally I prefer a greater level of decoupling between Actions and Model. This is a bit too coupled IMHO:
Proposals.case({
          Load: () => mutations.loadCountries().finally(() => {
            state.notifyChange(self)
          }),
          SelectCountry: (country) => mutations.select(country),
          UnSelectCountry: () => mutations.unSelect(),
          Filter: (filter) => mutations.filterCountries(filter),
          Sort: (isAscending) => mutations.sortOnName(isAscending),
          Pictures: (data) => mutations.setPictures(data.photos)
        }, proposal)

        state.notifyChange(this)
I also never use getters/setters on the model. SAM is best implemented with a reactive loop, the State function and Model work hand in hand. The goal of the State function is to translate the Model into view properties, so there is never a need for a getter. Setters are absolutely forbidden since only the model can mutate itself. No other part of your code should be able to "set" a value.
Concerns like Filter/Sort should not be considered mutation. They can be implemented in the state function as it create component properties, or in the view component itself.
Jean-Jacques Dubray
@jdubray
Jan 11 2018 23:16
I also would not run a query in the model.
 Load: () => mutations.loadCountries().finally(() => {
            state.notifyChange(self)
          }),
Even if it is simply to initialie the model. I would move the API call here:
load () {
    propose(Proposals.Load)
  }
The Model is a critical section that needs to run synchronously.
I do execute commands from the model which is logically the role of an action, but commands tend to be more synchronous.
Jean-Jacques Dubray
@jdubray
Jan 11 2018 23:31

Yes, this is one use of nap:

nexAction (model) {
    if (this.countrySelected(model) && !this.hasPictures(model)) {
      actions.fetchPhotos(model)
    }
  }

Even though this is the correct way to do it, I tend to do API orchestrations in the Action itself (selectCountry). I understand that it could be viewed as breaking the pattern. But as long as the temporal logic is easy to reason about, I would say why not? It is logically equivalent.

function selectCountry(e, present) {
      let country = e.country || 'samoa'
      propose({country})
      fetchPhotos(country).then( (pictures) => propose( { pictures, country } )
      // you may want to pass the country again here, such that the model can decide to accept the pictures
      // the country could have changed, in which case the model would ignore/reject the proposal
}
From a view perspective, using Observers is the right way to do it when the view is controlled by a mechanism like vdom. In general in my projects I use the State function to compute the state representation (~ generate HTML). Again, I understand people may not like it. It's ok.
Jean-Jacques Dubray
@jdubray
Jan 11 2018 23:37
When I work on Angular2 projects I use RxJS pub/sub mechanism.