These are chat archives for jdubray/sam

5th
Mar 2016
Stardrive Engineering
@HighOnDrive
Mar 05 2016 01:11

Staltz would care to differ I suppose, here is a tweet summarizing the observable approach he had just posted today:

"stream of DOM events ...mapped to stream of action objects ...mapped to stream of reducer functions …accumulated with scan: stream of state"

There is a lot of news emerging and this twitter site seems to capture a lot of the more interesting bits: https://twitter.com/reactiveIntent

Stardrive Engineering
@HighOnDrive
Mar 05 2016 02:21
Could you please correct the word "fonctions" across the SAM site. I read a lot and such poor attention to simple detail offends my eye and hurts my brain, thanks!
Gunar C. Gessner
@gunar
Mar 05 2016 02:43
as I understand everyone is invited to help out sending in PRs
Jean-Jacques Dubray
@jdubray
Mar 05 2016 03:42
@HighOnDrive Perhaps André could stop by to discuss?
Jean-Jacques Dubray
@jdubray
Mar 05 2016 04:38
@HighOnDrive no more "fonction"
Stardrive Engineering
@HighOnDrive
Mar 05 2016 05:02
@jdubray Thanks for the correction, it was a busy week and I just want to catch up on my reading :smile:
Not sure André would stop by, although he is likely watching. However, the need for more of a pattern is increasingly being discussed here: https://gitter.im/cyclejs/core
Jean-Jacques Dubray
@jdubray
Mar 05 2016 05:09

@HighOnDrive thank you for the pointer

it seems to me that the fact that we're making an HTTP request should not be a concern that a regular UI component should be dealing with.

:-)

I had done the same thing as (2) for React, with the ReaCall pattern:

(2) I've tried building an API in component (nested dialogue) form, with it being in charge of issuing HTTP requests and resolving the responses to state change emissions.

Jean-Jacques Dubray
@jdubray
Mar 05 2016 05:16
The game changer that SAM introduces is the "State" object. As discussed before and I know it is confusing, to call it "State" and further that "State" is actually ... stateless, but if we keep that aside, just for one second and hear the argument that have a list of property values is not enough to reason properly about a system. You also need to "compute" the (control) State of the system from the list of property values.

Otherwise, you end up here (@axefrog):

but because you're dealing with a tangle of streams, it can take longer to write and the bugs are harder to solve.

Nathan Ridley
@axefrog
Mar 05 2016 09:23
hey
just got the invite
i think you could technically refer to state within streams as "temporal projections", but that's a bit of a mouthful.
What is "SAM"?
Jean-Jacques Dubray
@jdubray
Mar 05 2016 12:36
SAM is a pattern that shares some of the same goals of cycle.js (decoupling precisely the business logic from the effects). It offers some advantages such as a nearly pure decoupling between the view and the back-end APIs.
@axefrog you can read more here: http://sam.js.org/
Milan Simonovic
@mbsimonovic
Mar 05 2016 15:49
V = S( vm(M.present(A(data))), nap(Model) )
this should probably be: V = S( vm(Model.present(A(data))), nap(Model) ) ?
btw vm is not defined anywhere
Jean-Jacques Dubray
@jdubray
Mar 05 2016 16:02

For instance, if the view displays a value v and a graphical indicator as to whether this value is great, good or bad, there is no reason to have the indicator’s value in your model: the function should simply compute the value of the indicator from the value v provided by the model.

Now, it’s not a great idea to directly embed these computations in the the view, but it is not difficult to make the view-model a pure function as well, and hence, there is no particular good reason to use GraphQL when you need an explicit view-model:

            V = f( vm(M) )
I'll add it to getting started guide
vm has no implication on nap,
Milan Simonovic
@mbsimonovic
Mar 05 2016 16:04
i'd argue that's part of the "application state" (UI), as opposed to the domain state (model)
just has to do with the UI, not the domain (business logic)
Jean-Jacques Dubray
@jdubray
Mar 05 2016 16:04
it's really about adapting the model to the components of the view
in general a component will be defined as:
function address(street,city,zip) {
...
}
vm(model) would adapt the model to whatever the view needs while keeping that logic reusable across different component.
Jean-Jacques Dubray
@jdubray
Mar 05 2016 16:10
The expression is not necessarily to take "literally", it's a logical representation of the pattern, that says, please consider all these elements when implementing the pattern and BTW, this is the order in which the elements are invoked.
Logically, the State creates the state representation, to it is using vm(Model) for that, rather than the Model directly. However logically the nap function has access to the whole Model.
Stardrive Engineering
@HighOnDrive
Mar 05 2016 18:37

Just catching up on reading and noticed this article: http://hannesdorfmann.com/android/model-view-intent

I think it reflects the general Cycle.js consensus, which is nicely put in the summary:

"See the thing with MVI is like with any other software architecture: MVI gives you an idea, but there is still space for personal preferences and interpretation. One can add as many layers as needed. For example, the model() function could internally be composed by multiple functions (one might call them use cases or interactors, just functional). One could add Action data structures to decouple things between intent() and model() even more. But keep in mind: don’t over-engineering things! Software architecture is a continuous evolution: Stay hungry, stay foolish, think outside the box!"

Jean-Jacques Dubray
@jdubray
Mar 05 2016 18:40
exactly, patterns are not libraries. The only difference between SAM and MVx patterns is that V is substituted for S and V becomes just a state representation. Some of the code that you'd be tempted to put in the View can now safely be implemented in the State function.
I guess André Statlz asked the question: "Why isn't better simpler" https://www.youtube.com/watch?v=1zj7M1LnJV4
Stardrive Engineering
@HighOnDrive
Mar 05 2016 18:51
Agree, patterns are where it is at. Part of the problem in nailing the right pattern is due to the scale of an app, for small apps a robust pattern is often pushed aside. André mentioned earlier today that he has not yet built a app that required a central state management facility, which proves again that we are all coming at things in our own way.
The "agendas" idea reminds me of a more convoluted way of what I'm doing with my state machine configurations. The dispatcher for all intents and purposes is just a well defined usage of the Intent level in the MVI pattern.
Stardrive Engineering
@HighOnDrive
Mar 05 2016 19:00
Would it not be possible to use the MVI pattern as a basis and then fill in between sub-stages? I think there is a opportunity for SAM to be embraced if it is builds on MVI and especially focuses on being a pattern for the new FRP paradigm being massively pushed by RxJS.
Stardrive Engineering
@HighOnDrive
Mar 05 2016 19:14

@jdubray You said, "The only difference between SAM and MVx patterns is that V is substituted for S and V becomes just a state representation. Some of the code that you'd be tempted to put in the View can now safely be implemented in the State function."

Why is state not just a facet of Model instead? That would scale better and not clash with everything everyone already understands. So then a Model could be big data in the cloud and fetched/cached aspects of the data via queries and/or also be dynamically reduced state in the way André likes to do things.

In other words in all scales along a data continuum. State is just what I call activated or active Model data, which can then be persisted as required.

Jean-Jacques Dubray
@jdubray
Mar 05 2016 19:17
The pattern is not prescriptive, but you can certainly implement it that way, by keeping the elements of the pattern distincts you give more choices (e.g. M implemented on the server, S+nap on the client)

Would it not be possible to use the MVI pattern

yes as a matter of fact if you look at Fred's implementation using Cycle.js, he chose to implement MVI.

actions are anemic (~intent):
const started$ = intents.start$.map(() => ({status: STARTED}));
And the SAM pattern is degraded in MVI:
{ DOM: view(model(intent(sources.DOM), sources.initialState)) }
you could just as well write:
{ DOM: view(state(model(action(intent(sources.DOM))),nap)), sources.initialState)) }
It's not incompatible, though I believe SAM provides a superior model.
Jean-Jacques Dubray
@jdubray
Mar 05 2016 19:22
That's why in particular people using Cycle.js cannot find a good place to plug APIs (they actually don't have a model to start with, no actions either), and then they don't have nap().
I am not quite sure why they are not jumping on SAM
The part I don't like about Cycle.js is to think that everything is cycle -> not quite, this is a big approximation.
I tend to not like monadic programming models (everything is a X = { class/object, function, actor, cycle, process, stream...}
Monadic programming models tend to transforms developers into (poor) boiler plate code generators...
Stardrive Engineering
@HighOnDrive
Mar 05 2016 19:33

I'm really looking forward to a comparison table between Cycle.js and SAM, there needs to be more revelation made.

I use Cycle.js but have from the start had a robust module system of actions and a place for nap() to happen. I agree with you about the "big approximation" and being "monadic".

Everything being monadic is not enough for real world solutions. One possible reason "why they are not jumping on SAM" is because it takes time to understand the cycle concept and ride out the steep RxJS learning curve. It's a case of not seeing the forest for the new and shiny trees, lol

Jean-Jacques Dubray
@jdubray
Mar 05 2016 19:40
Cycle.js provides some nice "wiring" and rendering capability, perhaps superior to anything I have seen before. But MVI underneath is too weak.
blob
How is the application state maintained in Cycle? From what I understand Fred used:
.scan((state, update) => {
is that the best way to do it?
Jean-Jacques Dubray
@jdubray
Mar 05 2016 19:50
On another note, there was a very interesting tweet this morning which I fully adhere to:
https://twitter.com/jamie_allen/status/706135604426317825
I believe what he meant (at least that's how I would translate it) you can't take actions without a clear understanding of the (control) state you are in. When a failure happens, you need to first update the model accordingly, then compute the resulting state, and react with the appropriate next-action. You can't do error handling on the spot in the action, model or view.
Stardrive Engineering
@HighOnDrive
Mar 05 2016 20:03

@jdubray You said, "is that the best way to do it?" and the answer seems to be yes! The focus is on the new and shiny RxJS trees and dynamically deriving state in a transient way only. On the side some Cyclists have made state-drivers but there is no real acceptance for a persistent model in the Cycle-verse.

André very much advocates that there is no more need for state as it has always existed and to just use RxJS to dynamically calculate state JIT. This of course makes Cycle rely on components to give structure to Cycle apps, instead of models. From there it's a decent down the same rabbit hole leading to co-location mud pies (components) and router maddness, sigh

Agreed, "you can't take actions without a clear understanding of the (control) state you are in"

Jean-Jacques Dubray
@jdubray
Mar 05 2016 20:22
@HighOnDrive I guess the things that I would challenge from an Rx's perspective is that (as I said before) there does not seem to be an easy way to block some of the intent/action streams. Again looking at Fred's code it looks like the model must subscribe to all actions/intents:
const started$ = intents.start$.map(() => ({status: STARTED}));
  const aborted$ = intents.abort$.map(() => ({status: ABORTED}));

  const counter$ = Rx.Observable.timer(0, 1000)
    .map(() => ({counterChange: -1}));

  return Rx.Observable.of(initialState)
    .merge(started$)
    .merge(aborted$)
    .merge(counter$)
    .scan((state, update) => {
In this code, the counter$ will continue to emit actions/intents and the model must understand the state to block them.
I don't find that natural at all.
Stardrive Engineering
@HighOnDrive
Mar 05 2016 20:30
@jdubray Right again, this blocking/filtering is nicely handled by control states and their activations in my solution :+1:
Jean-Jacques Dubray
@jdubray
Mar 05 2016 20:44
Anything you would want to share with us?
Stardrive Engineering
@HighOnDrive
Mar 05 2016 20:47
@jdubray Will consider it, presently fitting the whole reactive loop together from what I've confirmed from multiple sources.
Jean-Jacques Dubray
@jdubray
Mar 05 2016 20:53
thanks