These are chat archives for jdubray/sam

18th
Apr 2016
devin ivy
@devinivy
Apr 18 2016 00:24
@jdubray i can't stop thinking about SAM's actions– their apparent upsides and downsides. it sometimes seems awkward for an action to have to know so much about the model. imagine a single action having effects on multiple unrelated systems in the model? for example, a "logout" action should probably 1. clear my cart and 2. null the current user. the cart and the current-user system sshould manage themselves rather than an action needing to have a notion of both systems. the way i typically think of this is "stores" (parts of the model) subscribing to "actions" then the stores manipulating themselves as they see fit.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 00:27
actually, quite the opposite ... !!
that is the point of separating proposers and acceptors
devin ivy
@devinivy
Apr 18 2016 00:27

this makes sense,

Action logic can typically be reused across models, you can even think some companies will start offering SAM actions following a SaaS model. A "change of address" action, which returns a postal address given user data is highly reusable, across the enterprise.

that is an apparent upside. but i'm still struggling with the first part.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 00:28
all you need is an small adapter at the back of the action such that the dataset can be presented properly to the model
model.present( adapter(A(data)))
A(data) is the reusable action across models
you can even use OAuth to enable 3rd party actions to present to the model
the way i typically think of this is "stores" (parts of the model) subscribing to "actions" then the stores manipulating themselves as they see fit.
totally agree with you
what is proposed / accepted is subject to interpretation
devin ivy
@devinivy
Apr 18 2016 00:30
so the adapter would delegate the action's result to different subsystems of the model.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 00:30
proposed: logout = true
at that point the model could reject the logout and display something like "do you want to save your shopping cart for later?" (of course merchants save them for later by default)
the adapter can shape the data to match the output of the action to the input of the model
the wiring could dispatch the same action to different models
the key though is to keep the sequence propose / accept / learn even when you have multiple acceptors
the problem is that most programming models don't have a "learn" phase
devin ivy
@devinivy
Apr 18 2016 00:33
i see. so propose() might be making the same proposal to several models.
"the wiring" would be doing that.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 00:34
yes exactly
devin ivy
@devinivy
Apr 18 2016 00:34
perhaps a proposal could be a union-type...
Jean-Jacques Dubray
@jdubray
Apr 18 2016 00:34
you might have to deal with sync issues, but that's still not changing the paradigm
devin ivy
@devinivy
Apr 18 2016 00:34
(i loved that library you posted the other day.)
Jean-Jacques Dubray
@jdubray
Apr 18 2016 00:34
yes me too
devin ivy
@devinivy
Apr 18 2016 00:35
cool, this is very helpful– thanks again
Jean-Jacques Dubray
@jdubray
Apr 18 2016 00:35
as long as you separate proposal / acceptance phases and you respect the sequence, then good things should happen

@weepy asked:

How does the coupling of propose/accept apply here ?

one thing that Redux had to fix is precisely async actions, things that take time to compute the value to present to the model

Redux had to bring action creators / thunks + sagas, just to deal with that "little" question.
devin ivy
@devinivy
Apr 18 2016 00:42
haha, yep. i actually prefer reflux for this too. everything is just a listener or a publisher (or both, such as a store). so anyone or anything can listen to an action, which is one way to kick off async processes and more actions.
this is not akin to "promisifying" actions or anything like that.
devin ivy
@devinivy
Apr 18 2016 00:53
is it considered okay for nap() to have async processes?
Jean-Jacques Dubray
@jdubray
Apr 18 2016 00:56
yes absolutely, it is the action that presents its results to the model asynchrnously
now, you could have nap also trigger a synchronous action in 60 s
with SAFE you can even fire multiple actions and the first one that presents its value to the model wins
devin ivy
@devinivy
Apr 18 2016 01:01
i've heard that mentioned here, but i couldn't think of when i would use that.
a welcomed race?
Jean-Jacques Dubray
@jdubray
Apr 18 2016 01:04
Netflix gave a great example where the user triggers an action that takes some time to complete (specially on slower devices) and then changes its mind and clicks on something else
devin ivy
@devinivy
Apr 18 2016 01:04
ah! haha. that makes sense
Jean-Jacques Dubray
@jdubray
Apr 18 2016 01:05
you can even implement smart strategies (not supported by SAFE) where you only allow presenting data from the latest action selected
all this is rather trivial once you separate proposers from acceptors
why would you mutate your application state prior to gather the information needed to mutate it?
devin ivy
@devinivy
Apr 18 2016 01:06
do you imagine these systems being gradually accessed as an app matures? for example, starting with SAM, moving on to SAFE, then beyond.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 01:07
it really depends on the complexity of the app (UX / Back-end)
simple line of business app can probably be done with straight SAM
more sophisticated UX require more sophisticated wiring, that's when you need SAFE to make sure you are keeping the proposers under control
devin ivy
@devinivy
Apr 18 2016 01:08
i mean, what if an app gets to the point that it's using SAM but really needs features of SAFE. is that wiring simple enough that you could trivially make your existing SAM app a SAFE app?
Jean-Jacques Dubray
@jdubray
Apr 18 2016 01:09
yes, SAFE can be retro-added
devin ivy
@devinivy
Apr 18 2016 01:09
i guess i was really asking if SAFE is completely compatible with SAM, and it sounds like it is!
i would be very interested in an implementation of SAM that has a lot of the modular wiring already setup.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 01:10
SAFE's wiring is inserting itself in otherwise wired components
var actions = require('./actions') ;  
var model = require('./model') ;
var view = require('./view') ;
var state = require('./state') ;

var safe = require('sam-safe') ;

safe.init(actions,model,state,view) ;
devin ivy
@devinivy
Apr 18 2016 01:11
esp for multiple models and multiple proposals
cool! ^^
Jean-Jacques Dubray
@jdubray
Apr 18 2016 01:12
in the code above the actions, model, state and views were wired together, the init method inserts SAFE's middleware without knowledge of the components
It's a bit more complicated than just wiring, you also have the topology (client/server)
SAM 's code is naturally isomorphic unlike React
you can easily build your app in the browser and then move parts of it to the server
for instance the actions can run on the server and the model on the client
or action+model on server and state on client
or action+mode+state(view) on server
devin ivy
@devinivy
Apr 18 2016 01:15
that is a nice property. especially if actions could take place on client and server or peer-to-peer.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 01:16
yes, you can even mix and match, not to mention 3rd party actions via Oauth
SAFE also supports server-side time travel, so you don't have to worry (too much) about hot reloading
you can basically build a client app and set the model to any snapshot you want
SAFE sets up a default action/model/state on the server to do that, all you need is:
var safe = require('sam-safe') ;

// SAFE adds a default set of actions,model and state
safe.init() ;
You can use the same approach to do model synchronisation (client/server) or (server/server)
I just want to emphasize that once you use the proper factoring, all this happens rather easily, I am trying really hard not to produce a "framework"
devin ivy
@devinivy
Apr 18 2016 02:16
short of a framework, examples of different patterns are really useful. also, folks will naturally want to integrate the pattern into existing frameworks, such as react/angular/polymer etc (mostly view layers), and those integrations will all look different. you're already providing many useful examples. i do think there is some confusion around "the wiring" and how the wiring may differ between the simpler examples that already exist versus a large app with several modules/sub-projects/teams.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 02:21
the key is really to understand is that wiring does not work like in traditional framework or even React, because SAM truly prescribes V = f(M), then SAM works like mini code generators in which the proper wiring is "generated" to connect properly to the corresponding actions. After that the present() and render() are just simple wiring.
You've seen that sample where the action hooks are injected in the view components?
Jean-Jacques Dubray
@jdubray
Apr 18 2016 04:29
interesting to see how Jared opposes Redux with Relay/GraphQL
Anyone using Om/Next ?
use "pure components" as much as possible.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 04:35
blob
I'd be curious to see where people see SAM on this graph
Fred Daoud
@foxdonut
Apr 18 2016 11:54
@jdubray just finished catching up on the latest conversation. I find this all quite interesting, thank you for taking the time to explain the concepts. It's very helpful.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 12:04
thank you for the feedback!
Fred Daoud
@foxdonut
Apr 18 2016 12:21
@jdubray is there a good code example of NAP in jdubray/sam-samples (or elsewhere)?
Jean-Jacques Dubray
@jdubray
Apr 18 2016 12:22
the rocket launcher has a nap()
state.nextAction
Fred Daoud
@foxdonut
Apr 18 2016 12:27
state.nextAction = function (model) {
    if (state.counting(model)) {
        if (model.counter>0) {
            actions.decrement({counter: model.counter},model.present) ;
        }

        if (model.counter === 0) {
            actions.launch({},model.present) ;
        }
    }
}

state.render = function(model) {
    state.representation(model)
    state.nextAction(model) ;
}
Jean-Jacques Dubray
@jdubray
Apr 18 2016 12:28
yes
Fred Daoud
@foxdonut
Apr 18 2016 12:28
thank you
Jean-Jacques Dubray
@jdubray
Apr 18 2016 12:28
in this case the view will render
after the next action
on a server you'd need something like webSocket
Justin Litchfield
@litch
Apr 18 2016 12:37
That little toy I was working with yesterday has a nap too:
https://gist.github.com/litch/0b36eca4acb8d908ebb03efb5bc4eda8
I actually wrapped a few more layers on that and deployed it today in my production environment
(basically just put on hte headers and such needed for auth to my backend)
Jean-Jacques Dubray
@jdubray
Apr 18 2016 12:39
wow, you have an end point to share?
Justin Litchfield
@litch
Apr 18 2016 12:39
No, it's just for internal use
Jean-Jacques Dubray
@jdubray
Apr 18 2016 12:41
there is really no need for sagas, nap works just fine, without breaking the single state tree principle.
devin ivy
@devinivy
Apr 18 2016 12:42

wow, you have an end point to share?

have you heard of tonic endpoints? pretty great for sharing live code examples for nodejs. you'd write a snippet online, and it would act as both 1. a shareable code snippet and 2. a live HTTP endpoint.

Justin Litchfield
@litch
Apr 18 2016 12:44
Well my understanding, @jdubray, sagas are A way to implement a complex workflow. So if you have some end to end task that has to go through several steps corresponding to different systems, each system will know how to do the "work" on a given task, but not necessarily whether it should be forwarded on or whatever. I think that "Saga" as a synonym for "multi-step" (within a component/subsystem or spanning them) blurs some lines there
Jean-Jacques Dubray
@jdubray
Apr 18 2016 12:50
so does STAR, a JavaScript library that is compatible with SAM. Stateful orchestrations are quite difficult to code and reason about. STAR makes it very simple.
Justin Litchfield
@litch
Apr 18 2016 12:53
Thanks for pointing that out
devin ivy
@devinivy
Apr 18 2016 13:35
Screen Shot 2016-04-18 at 9.33.52 AM.png
here's a slide from that talk above. the example they give makes redux action-creators look a lot like SAM's actions, aside from the fact that presentation is done synchronously.
setColor() could contain logic to format what is presented to the model.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 13:37
yes I agree, it's interesting that Jared came to the same conclusion though people like Dan don't talk about "describe" vs "handle" or propose/accept
devin ivy
@devinivy
Apr 18 2016 13:39
that is interesting– it's certainly not emphasized as being important.
Fred Daoud
@foxdonut
Apr 18 2016 14:59
@devinivy what is the code font in that slide?
devin ivy
@devinivy
Apr 18 2016 15:01
the font? i'm not sure!
Fred Daoud
@foxdonut
Apr 18 2016 15:15
it's quite nice..
Fred Daoud
@foxdonut
Apr 18 2016 18:59
Apparently it's either Input Mono or Operator Mono.
Fred Daoud
@foxdonut
Apr 18 2016 19:08
action calls model.present(data)

  An 'action' consists of sending some data to the model, and the model decides what to do with it.
  In model.present(data), the model either accepts or rejects the proposed changes.

model.present calls state.render(model)

  The state.render function performs 2 operations:

    a. state.representation(model)
      this function in turn does 2 things:
        i. the state decides which view is the representation, and calls the appropriate view.x function to get that representation
        ii. the state calls view.display(representation) to render the view.

    b. state.nextAction(model)
      the state decides whether it is necessary or not to trigger another action.
@jdubray would you say this summary is accurate/complete?
brusand
@brusand
Apr 18 2016 19:14
And actions capture users data and/or API backends data
Fred Daoud
@foxdonut
Apr 18 2016 19:35
good point @brusand , where does an ajax call fit in there? my understanding from the discussion before is that it would be something like:
actions.goGetSomeData = (data, present) => {
  var data = data || {fetchData: true};
  present(data);
};

actions.receive = (data, present) => {
  // receive data from Ajax call, present it to the model
  present(data);
}

model.present = (data) => {
  if (data.fetchData && someOtherCondition) {
    model.fetching = true;
    state.render(model); // this ultimately displays "loading, please wait..." to the user
  }
};

state.render = (model) => {
  state.representation(model);
  state.nextAction(model);
};

state.nextAction = (model) {
  if (state.fetchRequested(model)) { // e.g. model.fetching === true && perhaps other conditions
    microAjax('http://localhost:3000', function(res) {
      console.log(res);
      actions.receive(res, model.present);
    });
  }
};

// Go!
actions.goGetSomeData(null, model.present);
brusand
@brusand
Apr 18 2016 19:37
Not exactly
Fetching is a state when i have an id and no data. In nap if fetching then call actions.fetch(id)
In actions.fetch get data from id and présent to model
Fred Daoud
@foxdonut
Apr 18 2016 19:42
@brusand can you show me by copy pasting code above with the adjustments you indicate?
brusand
@brusand
Apr 18 2016 19:45
Sorry i am on my mobile phone. Move your micro Ajax in the actions.getsomedata method with an id instead of null per exemple. and present thé data result to the model
No code in thé nap juste states ans call actions méthodes
Fred Daoud
@foxdonut
Apr 18 2016 19:49
@brusand is this better?
actions.goGetSomeData = (data, present) => {
  present(data);
};

actions.fetch = (data, present) => {
  microAjax('http://localhost:3000', function(res) {
    present(res);
  });
};

model.present = (data) => {
  if (data.id && someOtherCondition) {
    model.fetching = true;
    state.render(model); // this ultimately displays "loading, please wait..." to the user
  }

  if (data.fromAjax) {
    // accept the data from Ajax...
  }
};

state.render = (model) => {
  state.representation(model);
  state.nextAction(model);
};

state.nextAction = (model) {
  if (state.fetchRequested(model)) { // e.g. model.fetching === true && perhaps other conditions
    actions.fetch({}, model.present);
  }
};

// Go!
actions.goGetSomeData({id: id}, model.present);
brusand
@brusand
Apr 18 2016 19:49
Don t u
Fred Daoud
@foxdonut
Apr 18 2016 19:51
?
brusand
@brusand
Apr 18 2016 19:53
you can do better, and more in the SAM pilosophy :) the line in the present method fetching = true is a mess. fetching is a state of your model so you have to implement a state method called fectching(model) with return true when you have to fetch data (based on the the model you have a t this moment
now you have your state method , you can use it in the nap to call the actions getSomeData and all are independant of your model
brusand
@brusand
Apr 18 2016 20:01
Why do you call fetch in the nap méthod call getsomedata(id, présent)
Fred Daoud
@foxdonut
Apr 18 2016 20:04
actions.askToGetSomeData = (data, present) => {
  present(data);
};

actions.fetch = (data, present) => {
  microAjax('http://localhost:3000', function(res) {
    present(res);
  });
};

model.present = (data) => {
  model = merge(model, data); // accept the incoming data. might have more logic here.
  state.render(model);
};

state.render = (model) => {
  state.representation(model);
  state.nextAction(model);
};

state.representation = (model) => {
  var representation = "ERROR";

  if (state.isFetching(model)) {
    representation = state.view.pleaseWait(model);
  }
  else if (state.isDoneFetching(model)) {
    representation = state.view.showResults(model);
  }
  state.view.display(representation);
};

state.nextAction = (model) {
  if (state.isFetching(model)) { // determine isFetching state from the model
    actions.fetch({}, model.present);
  }
};

// Go!
actions.askToGetSomeData({id: id}, model.present);
so askToGetSomeData is the initial action, but not the action of actually doing the fetch. then state can determine isFetching and view is please wait. When in isFetching state the next action is fetch, that is why it is in NAP().
@brusand is that better? (thank you for your patience)
brusand
@brusand
Apr 18 2016 20:08
I use this kind of pattern a first view for loading request and use nap to load ... and refresh
Your code is OK to me
Now it is easy to add an initial getsomedata form , when the model has nom id , view cet data form,
When thé model has an id with ni data , view loading, and when you have data , view thé résultats, ... ans if you have a data and a timeout, you call the refresh action :)
Fred Daoud
@foxdonut
Apr 18 2016 20:13
:thumbsup:
brusand
@brusand
Apr 18 2016 20:14
Ans i prefer to code it in sam instead of ng-if, ng-show, ng-hide in the HTML angular templates
Sorry for error typings
Fred Daoud
@foxdonut
Apr 18 2016 20:16
No problem. I agree. I much prefer view = function(model)
devin ivy
@devinivy
Apr 18 2016 20:17
i cannot say i prefer to code it manually :P
brusand
@brusand
Apr 18 2016 20:19
I coded a d3.js dashboard and it is so easy with sam to implement d3 component auto refreshings themselves
What i like un sam, is you code very closed to your business logic and not pertubate with external contraint due to the framework you use
The value is your model not the framework
a nulcear-js "backend" to the frontend. any view layer can be used with it. it does not explicitly have the state vs. model separation from SAM, but it's a nice piece of work.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 21:54
In NuclearJS the reactor acts as the dispatcher, maintains the application state and provides an API for data access and observation.
I would warn against highly reactive architectures. I prefer a controlled reactive loop.
the whole idea behind SAM us that you never "access" the model (write or read). you present data on one side and the State function is responsible for the "reaction" to the mutation.
As I mentioned before the propose/accept/learn flow is not arbitrary and even though programming languages allow you to twist it any way you want, IMHO, you should not.
Jean-Jacques Dubray
@jdubray
Apr 18 2016 23:24
@foxdonut I am travelling today, will look at it later this evening