These are chat archives for jdubray/sam

12th
Jan 2018
boris sabadach
@bsabadach
Jan 12 2018 07:11
I’m also in different Time zone so just see @jdubray comments
david kaye
@dfkaye_twitter
Jan 12 2018 07:13
@jdubray I know we've gone over this before but just to clarify, models are synchronous, actions may entail asynchronous calls
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:13
@dfkaye_twitter corrrect
david kaye
@dfkaye_twitter
Jan 12 2018 07:14
--grrrr-- "may" entail async
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:14
I do consider commands to be synchronous, but that's not mandatory. Commands can be executed via nap
boris sabadach
@bsabadach
Jan 12 2018 07:14
but why @jdubray gave some examples in the acceptor function with model.CRUD?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:15
Because in general the user/UX percieves commands to be synchronous
you often want to know the result of the command before you can proceed to doing something else
image.png
david kaye
@dfkaye_twitter
Jan 12 2018 07:17
--grrrr-- my battery is running out
boris sabadach
@bsabadach
Jan 12 2018 07:17
?? what where did u found that . The Command design pattern is not supposed to be only synchronous, or I don’t understand what is your « command"
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:17
command ~ create, update, delete
as opposed to queries
david kaye
@dfkaye_twitter
Jan 12 2018 07:17
Will ping tomorrow
boris sabadach
@bsabadach
Jan 12 2018 07:17
but they are async!
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:18
yes, I am talking about it form a UX perspective
not from an implementation
As I mentioned, the correct way to do it is with nap and using a proper action.
boris sabadach
@bsabadach
Jan 12 2018 07:19
well when I’m creating an entity, I push the submit button and wit for the server to aknowledge an async way
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:19
yes, agreed, but from a UX perspective in general you want the front-end to block until you get the result
It not 100% true, but generally true
I never call a query from the model
boris sabadach
@bsabadach
Jan 12 2018 07:22
but why you gave exmaple with model.CRUD? there was an R
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:23
sorry where?
boris sabadach
@bsabadach
Jan 12 2018 07:27
wait I have to find
I can’t find it anymore but I’m sure I saw model.CRUD then follo
wed by model.postProcessing
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:31
yes, sorry
I remember now, it's in the boilerplate sample. This is the CRUD of the model, not APIs.
But even there, there is no R
boris sabadach
@bsabadach
Jan 12 2018 07:33
so u want to reproduce the CQRS pattern on the client side?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:33
Not quite
(API) Commands and Queries are related to actions (from a SAM's perspective).
Since SAM is a reactive loop, there is not get/queries to the model, the State functions computes props to pass to observers
boris sabadach
@bsabadach
Jan 12 2018 07:35
I’m lost, why some should be triggered form the model and some not?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:35
yes, sorry
So queries are triggered from Actions, the result is then proposed to the model which decides what to do. In your sample, the action will fetch a list of pictures and the model might decide to "accept" only the pictures of a certain size.
Commands are triggered from the model only for simplicity, they are logically associated to an action and will have a result (error, success) that ultimately will be accepted by the model.
boris sabadach
@bsabadach
Jan 12 2018 07:39
In fact I implemented the fetch of pictures in the select action action then I moved it the nap action because there was on synchronus proposal and one async in the same action and I thought it was weirf
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:41
If the (API) command is called from the model, the State function might be triggered twice, for instance:
... 
model.newReservation = { ... }
model.displaySpinner = true
axios.post( '/reservations', model.newReservation)
        .then( result => {
                 model.displaySpinner = false
                 state.render(model)
           })
          .cath( ... )

...
state.render(model)
@bsabadach yes, it's a bit weird, but I think it's more elegant and easier to reason than going through nap
nap should be used for actions that are decoupled from the original action that lead to that particular state
If you know the sequence, you might as well implement it in the action readily
boris sabadach
@bsabadach
Jan 12 2018 07:43
IMHO I would never use view layer semantics in the model layer. That’s why I changed a lot of wording; so I will not use model.displaySpinner
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:43
sure, I was just illustrating what would happen, it does not have to be implemented that way.
boris sabadach
@bsabadach
Jan 12 2018 07:44
same for state.render. That’s probably some people don’t quite get the pattern
You’r exmaple is the same that I wrote for load countries (btw I didn’t process eroors in the application) . So why do consider that populating a model is not a mutation?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:47
It is, but it's better to call queries from an action because they can be cancelled more easily
boris sabadach
@bsabadach
Jan 12 2018 07:48
how?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:48
In SAM the model is a critical section so you never want to be processing a new proposal until you have completed the processing of the prior one.
I created a very simple generic mechanism to cancel queries in SAM-SAFE
All you have to do is keep track of the step the API call was instantiated. If you want to cancel the call, you simply present a cancel proposal, the model step will be increment, when the action comes back it proposes the result but with the wrong step and the model is free to ignore it, cache it, ...
boris sabadach
@bsabadach
Jan 12 2018 07:51
ah ok I see better. But in the pictures you just posted what is the difference between the two orange arrows that point to queri
query
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:53
The fact that Redux or MobX do not have a good place to position the API calls is the main issue I have with their approach. I don't understand why Facebook for instance does not want to clean up that part of the programming model. It's even harder with React alone. They understand the problem because they came with Flux, but Dan doesn't seem to be an API guy.
100% of what I build is API based and SAM's primary focus was to decouple as much as possible the view from the APIs to avoid having the front-end drive the API signatures.
It creates tremendous pain on the back-end.
Jean-Jacques Dubray
@jdubray
Jan 12 2018 07:58
The decoupling between event/intent and action also creates some interesting possibilities in the way the application is structured, unlike Redux which wires events to reducers (Redux actions ~ events).
boris sabadach
@bsabadach
Jan 12 2018 08:01
I agree with that . That’s why i’m surprised you propose me selectCountry(e, present). Event should not be present in actions and should be processed( stop propagation and son on) in the view or component
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:02
Sure, I don't use DOM events, here e represents the payload from the call.
For instance:
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav navbar-right">
                        ${repeat(params.header.menu, 
                            (i) => i.href, 
                            (item, index) => html`<li id="menu-item-${index+1}"><a id="${item.href}" href="#" onclick="return setMenuItem({menuItem:'${item.href}'});">${item.label}</a></li>` 
                        )}
                    </ul>
                </div>
e represents: {menuItem:'${item.href}'} which is for me the application event (not the DOM event)
boris sabadach
@bsabadach
Jan 12 2018 08:04
ok but it should be better if it would be strongly typed, that’s why I used union-type
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:05
That's one of the reasons I really like functional HTML, you can structure you application events very easily.
Sure, I don't like types :-) but it's ok, I understand the rationale.
boris sabadach
@bsabadach
Jan 12 2018 08:06
So you find that couples the action toomuch with the model?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:06
the role of the action is to decouple the view event (application event) from the model, at some point you need some coupling.
I just like extensible data structures. I started writing code professionally in 1990 with Objective-C, so it's hard for me to think differently. But I am not trying to impose these views on everyone.
I am currently working on a sample that merges the internal state of components with the single-state-tree of the model (generically), such that the view components can always be functional/stateless and the model can logically access the entire state tree should it need to.
In the cruisade against global state, we picked the wrong solutions so far (objects and functions). At least that's what I believe.
boris sabadach
@bsabadach
Jan 12 2018 08:11
single-state-tree sound bad to by ears. If you r application (like redux) if relying on one global object, modularity is killed. There should as much SAM containers than domains
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:13
See, that's where extensible data structures come in, I have a single-state-tree but my application logic is never structured along a global tree. Every "domain" if you will can pick a different set of leaves. At no point the domain-bound business logic need to reason about the entire tree. I believe that approach to be vastly superior and help keep the complexity of a project within reasolable limits.
as soon as you create boundaries, they always get in the way. They can never be 100% right.
The building blocks of SAM: application events, actions, acceptors, reactors and renderers/observers can be slightly interleaved or even shared across domains
I do that all the time, this is incredibly healthy from an overall complexity's perspective.
boris sabadach
@bsabadach
Jan 12 2018 08:18
"Every "domain" if you will can pick a different set of leaves ». Sorry if you want reuse the module in another application or just throw it away a module would be isolated and should plug and play with the least possible refactoring. That’s the biggest problem of Redux IMHO
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:24
It's not a given, I understand your point, but:
1/ the decoupling between proposals and acceptors is where most of the heavy lifting will happen when you reuse some logic. In Redux, that's not a concern
2/ you can easily overwrite one of any action/acceptor/reactor while in Redux, a reducer would generally have all three types of application logic
That's why you start seeing more and more articles that are trying to fix Redux (FSM, Immer, ...)
boris sabadach
@bsabadach
Jan 12 2018 08:27
yes that’s why I prefer this way of building applications : https://mobx.js.org/best/store.html
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:28
I don't but that's ok.
boris sabadach
@bsabadach
Jan 12 2018 08:29
what is the problem with that except there is no « control state »?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:32
As I mentioned I don't believe boundaries can be designed, I like the flexibility of picking the subset of leaves that work for a particular context. I understand, that it doesn't look at clean as Michel's proposal, but that proposal does not reflect reality. It's like people who are trying to design microservices and use concepts like bounded context. On paper it looks great, until you hit real scenarios.
Data is relational and there is no way, absolutely no way to decompose a data model in domains.
boris sabadach
@bsabadach
Jan 12 2018 08:36
Of course yes! this is called Domain Driven Development and decouples DTO from databases. I recenly applied it in my former job! We Each domain as it’s own persistence support:
  • for search domain => elsatic search and proper API
  • from CRUD domain entities => SQL database
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:37
We'll have to agree to disagree.
boris sabadach
@bsabadach
Jan 12 2018 08:38
So you sore all your data’s in same persistence support?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:39
of course not, I am just talking about the inability to create data models that can sliced and diced in domains with no overlap whatsoever.
boris sabadach
@bsabadach
Jan 12 2018 08:44
but when you query a list of entities form elastic search or algolia, you end up with a denormalized model, how can you mix that then whith a CRUD for this entity?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 08:46
I am not sure I fully understand your question, are you talking about consistency?
boris sabadach
@bsabadach
Jan 12 2018 08:56
Since many years we query denormalized data from specialized API. This is what it is called the search domain . You have denormalized data and then for the CRUD you need normalized data. That’s why Facebook uses a trick. After having loaded their entire denormalized graph the apply https://github.com/paularmstrong/normalizr on it. How do you handle that?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:02
Perhaps I don't work on projects that are complex enough. I hear the problem, it is for sure better to exchange normalized data, but being a full stack developer, I design my APIs in ways that make general sense. I even propagate my concept of extensible data structures to the data access layer, so I don't have large SQL statements (poor's man ORM if you will) or complex mappings.
boris sabadach
@bsabadach
Jan 12 2018 09:06
ok I understand. And another question how do u handle API errors since you call API in actions. You pass an error proposal to the model?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:07
yes, exactly. Same thing for timeouts, I can initiate a setTimeout proposal and simply keep track which one comes first (or use SAM-SAFE's mechanism).
boris sabadach
@bsabadach
Jan 12 2018 09:10
Do u matter if ask form more questions? I understand we chat for a long time? what time is it at your home?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:11
please
4pm
I am on vacation until Monday.
boris sabadach
@bsabadach
Jan 12 2018 09:11
ah I’m unemplyed
unemployed until frebruary
so I have plenty of time !LOL
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:13
This time I took five weeks, nearly completely off, very little coding or reading
boris sabadach
@bsabadach
Jan 12 2018 09:15
a total break : always good
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:15
yes!
boris sabadach
@bsabadach
Jan 12 2018 09:16
well in Map.jsx I wrote that:
onMarkerClicked: (country) => { actions.unSelect() actions.select(country) },
is it correct to trigger two actions?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:18
Sorry, I had missed that one. In principle no, it would be best to handle the unSelect in the mutation. Any particular reason why you wanted to actions?
The fundamental pillar of temporal logic is that you have a step: action->mutation->state representation, and you can't start processing a new action until the mutation is complete.
boris sabadach
@bsabadach
Jan 12 2018 09:21
because U can ckick on markers on the map an it selects another country: descrition what updated but not pictures. Perhaps if I take back the picture loading form nap to select action it will work?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:22
probably, that's usually the benefits of SAM and the decoupling event/action, you can now trigger the same action from different events/intents
For sure, I would fetch the pictures in the action.
boris sabadach
@bsabadach
Jan 12 2018 09:25
btw you didn’t answered about the orange arrows on the schema. I don’t unders
stand
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:26
sorry, the dashed one should not be used
There might be a couple corner cases where it would make sense to call a query from the model, but I can't think of one.
boris sabadach
@bsabadach
Jan 12 2018 09:33
One other thing I don’t agree with not using getters in the model: it should be read only, that’s why there is the receive method for mutations. For me if you want to implement a form, you make a local copy in the view and then present it with an action and merge it with the original. Otherwise you won’t be able to implement a "reset form " button in your view
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:37
The way I use the pattern is that I generate HTML functionally. So the State function passes props to these view functional components. The model is never accessed or changed outside the present method (setter) and state function (getter). It's actually an anti-patter to "get" something from the model as it might be processing a proposal. For forms I general HTML with the corresponding model values.
Here is a real world example from one of my projects:
<form>
                <!-- div class="bg-mask"></div -->
                <div class="tabbable">
                    <div class="tab-content">
                        <div class="tab-pane fade in active" id="flight-search-1">
                            <div class="row">
                                <div class="col-md-3">
                                    <div class="form-group form-group-lg">
                                        <div class="form-group form-group-lg">
                                            <label style="color: ${color};">Round Trip
                                            <div class="btn-group btn-group-select-num" data-toggle="buttons">
                                                <label id="roundTripLabel" class="btn btn-primary ${roundTrip}" onclick="return actions.setRoundTrip({newRoundTrip:true,
                                                    ${gatherFormData}
                                                    });">
                                                    <!-- input type="radio" name="standby" value="1"/ --><i class="fa fa-check" style="position: relative; top: 10px;"></i></label>
                                            </div>
                                            </label>
                                        </div>
                                    </div>
                                </div>
                                <div class="col-md-4">
                                    <div class="form-group form-group-lg">
                                        <label id="roundTripLabel" style="color: ${color};">Drop Off / Pick-Up Only
                                        <div class="btn-group btn-group-select-num" data-toggle="buttons">
                                            <label class="btn btn-primary ${dropOff}" onclick="return actions.setRoundTrip({newRoundTrip:false,
                                                ${gatherFormData}
                                            });">
                                                <!-- input type="radio" name="standby" value="1" / --><i class="fa fa-check" style="position: relative; top: 10px;"></i></label>
                                        </div>
                                        </label>
                                    </div>
                                </div>
                            </div>
                            <div class="row">
                                <div class="col-md-6">
                                    <div id="FromZip" class="form-group form-group-lg form-group-icon-left"><i class="fa fa-map-marker input-icon"></i>
                                        <label style="color: ${color};">Departure City & State</label>
                                        <input id="fromCity"   class="typeahead form-control" placeholder="${defaultFromValue}" type="text" ${fromCity}/>
                                    </div>
                                </div>
                                <div class="col-md-6">
                                    <div id="ToZip" class="form-group form-group-lg form-group-icon-left"><i class="fa fa-map-marker input-icon"></i>
                                        <label style="color: ${color};">Arrival City & State</label>
                                        <input id="toCity" class="typeahead form-control" placeholder="${defaultFromValue}" type="text" ${toCity}/>
                                    </div>
                                </div>
                            </div>
                            <div class="input-daterange" data-date-format="m/d/yy">
                                <div class="row">
                                    <div class="col-md-3">
                                        <div class="form-group form-group-lg form-group-icon-left"><i class="fa fa-calendar input-icon input-icon-highlight"></i>
                                            <label style="color: ${color};">Departing</label>
boris sabadach
@bsabadach
Jan 12 2018 09:41
It doesn’t work with VDOM. You cannot get values easily unless you parse it recursivly
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:44
that's why I am not a big fan of all these vdom solutions. Have you looked into lit-html?
boris sabadach
@bsabadach
Jan 12 2018 09:44
and you use actions directly in the view? how can u prevent default on hyperlinks?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:46
sorry I don't understand the question, is it about e.preventDefault()?
boris sabadach
@bsabadach
Jan 12 2018 09:47
yes
I had a quick look on lit-html but only tried hyperHTML for the moment
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:50
yes, I struggled a bit with that one, I think my code doesn't work with FireFox.
My enterprise projects are Angular2 based. Let me double check how I got it to work (I think when the action returns false then Chrome and Safari consider it as "preventDefault")
boris sabadach
@bsabadach
Jan 12 2018 09:52
On every SPA all click on hyperlinks should be cancelled unless your page is reloaded, thats why I introduced « handlers » in my app to process the DOM event the way I want and than call the action
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:55
Yes, for some reason Chrome/Safari don't follow that part of the spec.
I can't find a preventDefault in all the samples I have open.
boris sabadach
@bsabadach
Jan 12 2018 09:56
with samples?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 09:58
my private projects
I just checked the largest I ever did in pure ES6 and I don't have any preventDefault. Just having the action return false is equivalent for Chrome and Safari.
boris sabadach
@bsabadach
Jan 12 2018 10:06
relying on return false is a little bit to violent for me; it does too much (stop propagation), and it seams it’s not well standardized https://stackoverflow.com/questions/30473581/when-to-use-preventdefault-vs-return-false
Jean-Jacques Dubray
@jdubray
Jan 12 2018 10:08
It's ok, I don't recommend doing what I do, that's outside the scope of SAM.
boris sabadach
@bsabadach
Jan 12 2018 10:09
Yes ok
Jean-Jacques Dubray
@jdubray
Jan 12 2018 10:11
Thank you for taking a look at SAM!
Always happy to have this kind of discussion and hear feedback, good or bad.
boris sabadach
@bsabadach
Jan 12 2018 10:16
Thanks for the chat; I took you a long time. I will refactor the app according your advices. The least I can say is that is very clear to code that way. I want be the closest to the pattern to proposethe app to my future new colleagues because they are using react but they are not very fond of Redux
Thanks a lot it took me time to understand mostly because of the semantics ( model.render, state.representation) and because I didn’t tried it. Now that’s clearer
Jean-Jacques Dubray
@jdubray
Jan 12 2018 10:18
Happy to continue the discussion any time!
boris sabadach
@bsabadach
Jan 12 2018 10:19
next step : implement a router with the pattern…...
btw your name sounds french?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 10:21
Oui!
boris sabadach
@bsabadach
Jan 12 2018 10:27
you are french?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 10:29
Je sais pas trop, j'ai vecu dans pas mal de pays. Je parle Francais et mon passeport est Francais.
boris sabadach
@bsabadach
Jan 12 2018 10:30
LOL
Jean-Jacques Dubray
@jdubray
Jan 12 2018 10:31
J'ai vecu juste 15 ans en France.
boris sabadach
@bsabadach
Jan 12 2018 10:36
Il faut revenir la France est devenu le deuxième pays derrière les US en terme de nombre de startup! Et puis on n’a pas un président, on a un demi dieu, ça galvalise les foules!
Jean-Jacques Dubray
@jdubray
Jan 12 2018 10:38
ah ah. Oui c'est complique, enfin c'est sur qu'avec la situation au US je suis content de n'avoir qu'une nationalite.
boris sabadach
@bsabadach
Jan 12 2018 10:46
Bon a bientôt
Jean-Jacques Dubray
@jdubray
Jan 12 2018 11:18
A+
Rob Siera
@robsiera
Jan 12 2018 15:10
started watching the "humor and science" video you suggested. Very funny and interesting indeed.
boris sabadach
@bsabadach
Jan 12 2018 15:20
Jean-Jacques Dubray
@jdubray
Jan 12 2018 16:29
@robsiera :+1:
Łukasz Lityński
@hex13
Jan 12 2018 17:50
basically libraries like Immer or Transmutable "listen" to your assignments. Using such libraries - when you write foo.bar = 3 there is no immediate mutation but it's something like setter. And this setter "does some magic" to mark changes you intent to do. The same with referencing - using such libraries when you reference e.g. foo.barit does not return foo.bar right away but performs some sort of lookup
it's like getter
Łukasz Lityński
@hex13
Jan 12 2018 17:57
generally libraries solve two problems 1. we need immutable structure (I suppose @jdubray won't agree with this ;) ) 2. mutable coding is more friendly to programmer
These are two opposite trends in JS state management.
immutable Redux and mutable Mobx
these are two separate words
but if you try to connect the dots it turns out that the problem with Redux can be solved by solution proven in Mobx
Łukasz Lityński
@hex13
Jan 12 2018 18:03
or Vuex
but I'm not sure if this is a really "easy way"
because creating such library is pretty hard
you have to think about many edge cases
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:17
@hex13 yes, I disagree! These libraries only exist because we don't understand that:
1/ we need to separate the computation of the proposal to mutate from the mutation itself, and ideally from computing the state representation as well
2/ Mutations are part of a programming step
They perpetuate the fallacy that a mutations and assignments are equivalent. They encourage developers to be sloppy for zero gain, perhaps even negative value since you still have to debug that horrific spaghetti code.
Łukasz Lityński
@hex13
Jan 12 2018 18:21
but they are not equivalent. If you write foo.bar = 1234 this is no mutation
but this is method calling
in proxy
BTW what is proposal?
I see this in this way
you call foo.bar = 1234
Łukasz Lityński
@hex13
Jan 12 2018 18:26
then a set trap is called in ES6 Proxy
and then you can do whatever you want
you can replace foo.bar = 1234 into "proposal" of new value of bar property
SAM-like (if I understood SAM correctly)
you can also trigger some kind observers
notify them about change
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:30
@hex13 that's my point you need to understand the basics of temporal logic, and the primed operator is the most important concept (proposal). You can also look at the Paxos protocol, there is no math there. It works the same way: propose, accept, learn.
A primed variable contains the value in the next state. So if your model has the variable a, a' will contain the value of a in the next state while a will contain the value of the current state. When you perform a mutation you need to keep both handy, and the computation of a' cannot start the mutation. The whole purpose of an action is to compute the primed variable values and "propose" them to the model.
There is no "proxy" or immutable strategy that aligns with temporal logic.
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:37
In other words, the next state relation is a function of primed and non primed variables. You can't make this stuff up. In other works the next-state-relation is not a pure function of an event as Redux it is. The "reactive" view of mutability that Mobx offers is also wrong. It works in some cases, but it is generally wrong.
you just can't reduce mutations to a series of assignments. Proxies and immutability will change nothing to that statement.
Łukasz Lityński
@hex13
Jan 12 2018 18:45
so primed values from TLA+ are proposed from SAM?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:45
yes!
Łukasz Lityński
@hex13
Jan 12 2018 18:47
"So if your model has the variable a, a' will contain the value of a in the next state while a will contain the value of the current state. When you perform a mutation you need to keep both handy, and the computation of a' cannot start the mutation." - I don't know what you meant exactly
but
back in 2013
and before actually
I was making some browser games
I had to calculate new positions and properties of game objects
during e.g. collision detection
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:49
yes, I see exactly where you are going. That's exactly it.
Łukasz Lityński
@hex13
Jan 12 2018 18:50
and if I checked e.g. 100 different objects I couldn't write just obj.x = 10; obj.y = 20 because data consistency of all object list would be broken
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:50
You need to know the value you are going to mutate to, to actually perform the mutation.
Łukasz Lityński
@hex13
Jan 12 2018 18:50
because e.g. half of objects would be in inconsistent state
and I separate a "new state" from "old state"
and I wrote something like obj.newState.x = 10; obj.newState.y = 20;
and then on the beginning of frame I made a newState current one
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:51
absolutely. In a SAM action you have not done any mutation yet.
I kept old data and new data separately and only assign to new data and read from old data
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:54
The key is really to understand that a mutation is better expressed by:
nextState.a = currentState.a + nextState.b * currentState.c
Without that distinction between currentState and nextState values you'd be lucky to get it right every time.
Łukasz Lityński
@hex13
Jan 12 2018 18:56
but this is possible to do with Proxy. My previous version of Transmutable was transactional, i.e. data in proxy always was point to the old version, and only after commiting all mutations was applied at once.
But this was unintuitive and have some issues e.g. with arrays
so now Transmutable applies mutations immediately
but I think this is not a big problem
mutations can be undone (because this is proxy, not real object)
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:58
Again, this is not how mutations work, a next-state value is a function of current-state and next-state variables.
Łukasz Lityński
@hex13
Jan 12 2018 18:59
additionally I could e.g. pass reference to the old state into transforming function
Jean-Jacques Dubray
@jdubray
Jan 12 2018 18:59
that cannot be expressed without an explicit relation
Łukasz Lityński
@hex13
Jan 12 2018 19:00
nextState.a = currentState.a + nextState.b * currentState.c - why half of this variables are from current and haf from next state?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:00
please look at this formula: nextState.a = currentState.a + nextState.b * currentState.c
Fred Daoud
@foxdonut
Jan 12 2018 19:00
the key is the separation of the "proposal" and "accept". The Model "accepts", and is responsible for applying the mutation. The State then provides a representation of the Model.
This is a very explicit, clear, deliberate structure that can be implemented with very plain code (free of any magic.)
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:00
because that's how a mutation works.
Łukasz Lityński
@hex13
Jan 12 2018 19:00
+ and * are just example operators?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:00
yes,
Fred Daoud
@foxdonut
Jan 12 2018 19:01
On the other hand, Proxys, MobX etc. that try to "instrument" statements like obj.x = 5 are just putting a blanket over the problem and hiding it with magic.
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:01
@foxdonut absolutely
And I am not even talking about API calls.
Łukasz Lityński
@hex13
Jan 12 2018 19:03
nextState.b * currentState.c I don't understand why b from next state should be multiplied by c from current state. Why not nextState.b * nextState.c or currentState.b * currentState.c ?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:04
Because this is how the next-state relation works on average, you can rely on arbitrarily making assignments in any order.
There will be also a lot of if-then-else statement based on current/next state
You need that kind of structure to write your code, otherwise it will be very difficult to write.
Łukasz Lityński
@hex13
Jan 12 2018 19:06
let's have, for example model of rectangle and variables width, height and area deduced from the width and height. It could have more sense to me to write
newState.width = 10;
newState.height = 10
newState.area = newState.width * newState.height
in this example it would be even incorrect to write newState.area = oldState.width * oldState.height
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:08
There are a lot of systems where the next state is independent of the current state (I can just assign new values and be done with it) and there are just as many where the next state is a complex formula. You can't make this kind of generalization.
Łukasz Lityński
@hex13
Jan 12 2018 19:09
interesting. I will think this through
devin ivy
@devinivy
Jan 12 2018 19:10
the fact of the matter is that we use immutable data in js client-side code for performance reasons. everyone says it's because using immutable data helps avoid bugs. but it's really because of how react-redux's connect() and react's PureComponent work...
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:11
that's temporal logic, that covers all the systems that are time-dependent aka stateful. That's why stateful code is hard to write, TLA+ makes it easier to write (but it is still hard). Immutability or proxies offers not temporal structure at all.
@devinivy yes, that has been my point all along, React is too reactive! you need to mutate first then render, otherwise React will start rendering randomly.
devin ivy
@devinivy
Jan 12 2018 19:11
in other words, the performance implications of the view layer are determining how people write their model
which is really too bad!
but that's the direction the tooling has gone. with exceptions, like SAM principals and meiosis :)
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:12
at least they should not pretend that this is a superior state management solution, it is not.
devin ivy
@devinivy
Jan 12 2018 19:12
i absolutely agree with that
no doubt about it
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:12
:+1:
devin ivy
@devinivy
Jan 12 2018 19:13
unfortunately there are some view layers that lean on mutation (polymer) and others that rely on immutability (react)
that's why i think a good model should work with both
if you can get your model and state to work with both react and polymer, congrats! that's how things should be!
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:14
yes, absolutely.
devin ivy
@devinivy
Jan 12 2018 19:14
mobx-state-tree and before that cerebral's state tree really do address this! meiosis also does, from a different perspective.
boris sabadach
@bsabadach
Jan 12 2018 19:15
hum
devin ivy
@devinivy
Jan 12 2018 19:15
one big trick is handling models that have circular references– serializability is worth considering.
and that's one thing you get for free with immutable data
boris sabadach
@bsabadach
Jan 12 2018 19:15
@devinivy what’s the difference between model and state?
devin ivy
@devinivy
Jan 12 2018 19:16
under SAM, the model is in a sense "normalized"– there isn't any duplicate information represented in the model.
state is a representation of the model adapted to be useful to the view
Łukasz Lityński
@hex13
Jan 12 2018 19:16
is model a logic or data or both? In classic MVC model is like logic + data
devin ivy
@devinivy
Jan 12 2018 19:17
the model is both logic and data!
boris sabadach
@bsabadach
Jan 12 2018 19:17
yes I agree
devin ivy
@devinivy
Jan 12 2018 19:17
the model receives proposals to change its data, then processes those proposals into the next "step" of the model
in redux it's roughly a reducer
in redux state is essentially a selector
but in SAM state is more than just a representation of the model adapted for the view
it can also fire more actions/proposals!
that's what nap() is
boris sabadach
@bsabadach
Jan 12 2018 19:19
I think that there is a problem in front end community with what state means
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:19
we usually resolve it by talking about state representation and control state
devin ivy
@devinivy
Jan 12 2018 19:20
in my opinion the most important takeaway from SAM is to have a layer between your model and your view– we call that "state" or "state representation"
boris sabadach
@bsabadach
Jan 12 2018 19:20
yes you but not in redux, mobs or vuex
devin ivy
@devinivy
Jan 12 2018 19:21
true! in mobx it's "computed"
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:21
It's really three things:
  • mutation/temporal logic (of actions)
  • APIs
  • state representation
Łukasz Lityński
@hex13
Jan 12 2018 19:21
SAM state is something like presenter in MVP?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:21
You can't really dissociate them
Łukasz Lityński
@hex13
Jan 12 2018 19:21
or View-Model?
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:22
except that there is no two-way binding
Łukasz Lityński
@hex13
Jan 12 2018 19:22
or something people write in mapStateToProps in Redux ?
or selector in reselect?
boris sabadach
@bsabadach
Jan 12 2018 19:22
from wikipedia: "Similarly, a computer program stores data in variables, which represent storage locations in the computer's memory. The contents of these memory locations, at any given point in the program's execution, is called the program's state."
Łukasz Lityński
@hex13
Jan 12 2018 19:22
state is evil
these are just definitions
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:24
@bsabadach it's better to reason in terms of what is not covered in that definition
Łukasz Lityński
@hex13
Jan 12 2018 19:24
according to the Webster Dictionary state is like mafia or junta a : a "politically organized body of people usually occupying a definite territory; "
so basically junta
boris sabadach
@bsabadach
Jan 12 2018 19:24
well ok but when we use words we have to share the same definitions.
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:24
For instance control state is not just a value in a variable. It has implications in terms of what actions are allowed.
Łukasz Lityński
@hex13
Jan 12 2018 19:24
but this turns political
boris sabadach
@bsabadach
Jan 12 2018 19:25
Isearch on google "state in computer science"
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:25
Also some property values are used to express relations, which have again more semantics than just a value in a variable
boris sabadach
@bsabadach
Jan 12 2018 19:26
and also this one on the same page :"In information technology and computer science, a program is described as stateful if it is designed to remember preceding events or user interactions;[1] the remembered information is called the state of the system."
Jean-Jacques Dubray
@jdubray
Jan 12 2018 19:26
I have to sign off, interesting discussion!
Łukasz Lityński
@hex13
Jan 12 2018 19:28
BTW state in finite state machines seems to be something even different (e.g. one word "closed", "open")
and state machines are often drawn as graphs, where each node is one state
and the other hand state from e.g. Redux it's more like a one big tree
boris sabadach
@bsabadach
Jan 12 2018 19:29
Redux is only data in memory
Łukasz Lityński
@hex13
Jan 12 2018 19:29
and there is no determined "state names"
like in FSM
boris sabadach
@bsabadach
Jan 12 2018 19:31
pearhaps they it’s an Infinite State Machine
Łukasz Lityński
@hex13
Jan 12 2018 23:15
https://en.wikipedia.org/wiki/Lamport_timestamps wow. on this article on wikipedia it seems pretty simple, these Lamport counters
"For any two events, A and B, if there’s any way that A could have influenced B, then the Lamport timestamp of A will be less than the Lamport timestamp of B. It’s also possible to have two events where we can’t say which came first; " pretty reasonable
(I stumbled by accident into article on Lamport. I searched for something about how to do collaborative editor, operational transformation and this kind of stuff)
and surprisingly I've seen Lamport name
It's kind of surprise that there is such simple solution for ordering events, maybe it could may be useful in e.g. multiplayer games
devin ivy
@devinivy
Jan 12 2018 23:26
"For any two events, A and B, if there’s any way that A could have influenced B, then the Lamport timestamp of A will be less than the Lamport timestamp of B. It’s also possible to have two events where we can’t say which came first; " pretty reasonable
this is essentially the definition of a poset / "partially ordered set"
this definition of events is actually really common, i.e. in physics!
Łukasz Lityński
@hex13
Jan 12 2018 23:27
but I'm not sure this solves all problems.
let's have two computers
working offline
on some graphic document
first user draws a circle
so counter would be then 1
after connecting to network and sending event to other computer
but
second user draws a circle, rectangle, triangle
so after connecting to network and sending it, it would send circle: t=1, rectangle t=2, triangle t=3
and first one receives
and both people do this for e.g. 5 minutes
but it looks like triangle t=3 is later
than everything first user did
Łukasz Lityński
@hex13
Jan 12 2018 23:32
(well in this case it won't matter, but let's assume maybe that first user after drawing circle clicked "clear all document"
so first user: circle: t=1, erase all: t=2
second user: circle: t=1 triangle: t=2, rectangle t=3
wait (I'm thinking loudly)
if have correct understanding
first user after erasing all, will receive messages, but two first messages will seem to be in the past (circle t=1, triangle t=2), so what? It would apply on message which has appropriate counter i.e. rectangle t=3
Łukasz Lityński
@hex13
Jan 12 2018 23:38
and first user having clearing all document will be drawing rectangle from second user ?
it wouldn't have too much sense, because second user drew 3 shapes, so it should draw 3
on the other hand first user clicked "clear all" so it should be cleared
I suppose I don't really understand all the implication of these counters
Łukasz Lityński
@hex13
Jan 12 2018 23:47
I mean something like this
image.png
I suppose even from UX perspective it would be hard to decide
Łukasz Lityński
@hex13
Jan 12 2018 23:53
though. Common sense. If first user drew orange rectangle and clicked "clear all" then only orange rectangle should be cleared, because green rectangle, circle and triangle was not drawn yet (from the first user point of view)
so I think in the end only changes from second user should be visible, like this (because first user cleared their changes)
image.png
but this is common sense, I don't know how Lamport timestamps are related to this