These are chat archives for jdubray/sam

7th
Nov 2016
Jean-Jacques Dubray
@jdubray
Nov 07 2016 00:29 UTC
@chuckrector here is the PDF link if you don't have access to iBooks.
Charles Rector
@chuckrector
Nov 07 2016 02:39 UTC
great, thank you 🙏
Charles Rector
@chuckrector
Nov 07 2016 03:36 UTC
i finished a v6 of my scene editor that can size the sprites with some grips https://github.com/chuckrector/sam-study/tree/master/scene-editor
Charles Rector
@chuckrector
Nov 07 2016 03:41 UTC
hmm, i should add inputs for height now
Charles Rector
@chuckrector
Nov 07 2016 03:49 UTC
in v5, there was only dragging, and changing the width in the inputs kept a perfect aspect ratio. but for v6, i had to track both width & height for full sizing control. initially, i used nap() to loop thru all sprites after each render to see if any sprites had no height set (since i was letting the browser do the heights previously, and only settings widths) -- if there was no height, that would mean it was a freshly added sprite and i needed to wait for its image to load and then read its height and set it on the sprite. so waiting for assets to load and then parsing/reading/whatever from them seems like a good "automatic action"
Charles Rector
@chuckrector
Nov 07 2016 03:54 UTC
it was funky at first, and i was kicking off a caculateSpriteHeight action in the state function, but also using jquery to wait for the sprite's image to load and in the callback kicking off another doneCalculatingHeight action. but then i realized probably i only needed the former action in there, and all that other jquery stuff could be moved out into the calculateSpriteHeight action itself. wasn't sure if that was a bad idea -- presenting to the model, but also kicking off another action after some async occurred
but then i realized by the time the user is ready to resize the thing, the image is already loaded anyway, and instead i could just read the current height of image when converting mousedown into appropriate actions -- so now in mousedown i figure out which sprite's grip i'm clicking on, inspect the DOM to find the sprite's image's current height, and then fire both a setSpriteHeight action with that height, as well as the beginSizingSprite action. a sort of JIT setting of the height i guess, heh
Fred Daoud
@foxdonut
Nov 07 2016 13:15 UTC
I would not say "centralized", it's more like "deliberate", the step during which you mutate state must be delineated. You can't arbitrarily/liberally assign property values. That is (IMHO) what's wrong with the way we write code.
That is the biggest problem with two-way binding (Angular, Vue and others).
Jean-Jacques Dubray
@jdubray
Nov 07 2016 16:02 UTC
@foxdonut absolutely, that is why two-way binding is an anti-pattern

@chuckrector why is it that the model has to know about the sprite until it's loaded? Can't you write an action where the proposal will include the new sprite, including it's action?

addSprite( url ) {
     request(url, function(data) {
          let height = h(data);
          let width = w(data);
          present( { newSprite: { data, height, width}}) ;
     });
}

I really don't see the need for nap() to be involved. That's kind of the big value add of SAM, you need to inform the application state of what's happening (unless it logically requires that you do so)

Jean-Jacques Dubray
@jdubray
Nov 07 2016 16:07 UTC
Am I missing something?
Rick Medina
@rickmed
Nov 07 2016 16:07 UTC
@jdubray what do you mean by "arbitrarily/liberally assign property values"? are you including redux in that pattern?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 16:08 UTC
you have to define a perimeter around what mutation means
you cannot just say, just like in the case of @chuckrector 's example
sprite[next] = data
width = 200
height = 100
you have to isolate the assignments from the side effects
It's kind of obvious, but harder said than done, unless you use an approach like SAM
You easily get lost, because the boundary is not so easy to draw. SAM allows you to build that boundary by design.
Rick Medina
@rickmed
Nov 07 2016 16:13 UTC
@jdubray mmm I'm getting lost between these two statements: "you have to define a perimeter around what mutation means", "you have to isolate the assignments from the side effects"....
how would you define side effect?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 16:20 UTC
@chuckrector 's problem is that the sprite is added but without the correct width/height, possibly causing some UX issue. From a SAM pattern's perspective, the side-effects start with whoever needs to "learn" from the mutation. As I said, it's kind of obvious, you need to reach a stable "state" before other elements of the program can do "react" to it, but that's not obvious to achieve in practice.
You quickly get lost as to what a "stable" (i.e. mutated) state means.
Fred Daoud
@foxdonut
Nov 07 2016 16:31 UTC
Yes, yes! It's a huge win in SAM to control mutation and the reactive loop, without tying together the model and the means of controlling mutation. I am trying to say that, although the "model" in SAM controls mutation, the model data itself does not, i.e. it is a plain simple JavaScript object. IMHO this is the problem with observables, RxJS, MobX etc. The model data and the way of controlling mutation are mashed together, and suddenly you have to use these specific types of objects for your model instead of plain JS objects.
Instead of having these special observable objects along with their APIs etc, you just present a proposal to the model, the model handles mutation, and the state provides the representation. The actions and view are totally decoupled, they don't need to know about anything other than plain JS objects.
Jean-Jacques Dubray
@jdubray
Nov 07 2016 16:33 UTC
yes, all these approaches are "too reactive", the point is not to be reactive, in itself that's not such a big, goal, at best an optimization. The point is to control mutation
Fred Daoud
@foxdonut
Nov 07 2016 16:35 UTC
I did a quite large project with CanJS (a few years ago, which are decades in JavaScriptLand), and, although it lead to better code at the time, the one thing that was a real drag was that you had these observable objects instead of plain JS objects.
I find it a much saner way to write programs, to follow the very clear present/accept/learn loop rather than observable.setValue/magic happens/things get updated/no idea of all the impacts.
Another testimony to the sanity of SAM is, how easy it is to write a tracer/devtool/time-travel tool vs writing one for cyclejs (for example).
Rick Medina
@rickmed
Nov 07 2016 16:41 UTC
I understand/agree with all of the above but I still don't get this "arbitrarily/liberally assign property values". SAM guarantees that state1 -> state2 transitions are valid bc it has access to the whole model (its centralized). I don't see how it would do that if that wasn't the case
Fred Daoud
@foxdonut
Nov 07 2016 16:43 UTC
@rickmed if you do model[property] = "newValue"; anywhere instead of only in the model.
Rick Medina
@rickmed
Nov 07 2016 16:47 UTC
@foxdonut correct. So the "context/boundary" of mutation in SAM (in the present function) is that it checks the current state to whether where/how to mutate it (forward to new state), right?
Fred Daoud
@foxdonut
Nov 07 2016 16:48 UTC
yes
Rick Medina
@rickmed
Nov 07 2016 16:49 UTC
if it hadn't access to the whole current state (model), how could it have this mutation context? that is why I argue that it is a centralized system (as opposed to redux for example)
Jean-Jacques Dubray
@jdubray
Nov 07 2016 16:57 UTC
There is no concept of "centralization", this is just a common/safe way to implement the pattern. I can have parent/child instances, I can even have several present running in parallel if I know what I am doing. it should rather be viewed as a protocol with clear roles and responsibility.
Rick Medina
@rickmed
Nov 07 2016 17:02 UTC
again, if the model doesn't have access to the whole current state (model), how could it have this mutation context? and such guarantee valid transitions state1 -> state2
Jean-Jacques Dubray
@jdubray
Nov 07 2016 17:04 UTC
All you need is the relationship proposal / unit of mutation / state representation, there is nothing that says that every proposal has to see/impact the entire model. There is absolutely no requirement for that. Again this is just a common way to implement SAM.
I could implement SAM with a bunch of proposal/unit of mutation/state representation, it's not a good coupling, but I could couple proposals with units of mutation and the parts of state representation they impact.
Rick Medina
@rickmed
Nov 07 2016 17:13 UTC
what exactly is a unit of mutation?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 17:14 UTC
this is where you are making the assignments model[property] = "newValue";
Rick Medina
@rickmed
Nov 07 2016 17:50 UTC
honestly, I think it is a bit hard to understand these concepts (specially where you can have multiple implementations) without concrete implementations and examples -and what implications designs you have until you implement it in concrete examples and/or large scale. But this is interesting " could couple proposals with units of mutation and the parts of state representation they impact." is there an example of that?
also, does sam prevent +1 single sources of truth (atom/model)?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 17:51 UTC
it just means your have several "present" methods that could potentially execute in parallel because the units of mutation/state representation independent.
You can imagine a dash board each widget is kind of independent of each other. You could decide to have one big model behind the dashboard or one per widget. As long as the actions on one widget does not have implications on another, you can keep everything separate. Same thing for a game, you could have different independent planes without interactions between them.
Again, there is no requirement for centralization, this is just a better way to implement the pattern, because in general the units of mutation are not that isolated and their boundary can change over time, it would make it harder to refactor over time.
However, there is no conceptual reason for having a centralized model.
the way the SAM patterns work is proposals (prepare) -> units of mutation (mutate) -> state representation (propagate the result of the mutation)
Jean-Jacques Dubray
@jdubray
Nov 07 2016 18:10 UTC
@rickmed Please look at it from the most basic point of view, SAM simply encourages you to group assignments in "units", no other parts of the code can make these assignments and no other parts of the code can see these assignments until the unit is complete.
as @foxdonut you could enforce that with specific types such as "observables", "streams" or "immutables" but that brings a lot of constraints to what you are trying to do, they don't make these problems go really go away, especially RxJS.
Rick Medina
@rickmed
Nov 07 2016 18:19 UTC
@jdubray so for example, the nap function is not mandatory in sam? only the present function (unit assignments)?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 18:24 UTC
yes it is, each time an automatic action needs to be triggered, it needs to happen at that point in time, but you don't need a "centralized" decision process. You just can't trigger an action in the middle of a mutation, that's why I say SAM is closer to a protocol, rather than a structure.
Charles Rector
@chuckrector
Nov 07 2016 18:58 UTC
@jdubray yes, it would make sense to load the image dimensions as part of an addSprite sort of action
though that would also require me to make some changes to my initial model that hydrates the real model. but that is i suppose because the new requirement of height for each sprite would make my current initial model data invalid
that is, any image has its "natural size" and in my data i am only passing a single "desired width" as a proxy for a scaling percentage. so in v5 the height of the sprite's image was always implicit and irrelevant to UX -- you just specified different widths and the image would naturally scale according to its aspect ratio, as a result of the fact that browsers auto-size a non-specified dimension in proportion to any specififed dimension
in v6, height is now important because you can size any sprite's image arbitrarily, ignoring its natural aspect ratio. the funky states i got into were surely a result of just hacking away instead of stepping back to think about the impact of the new UX on the model
Charles Rector
@chuckrector
Nov 07 2016 19:07 UTC
now that sprite height is a "first class citizen" that means i should... update my "initial model" to include heights too. normally, any initial model you would use to hydrate your app would have been the result of a previous saved state, and this wouldn't play mind games. but on a small toy example, since i have handcrafted the initial model myself, i have to keep a close eye on what i am trying to perform my initial model population with... since the initial assignment is basically one huge unchecked mutation of state
i should probably be validating that initial model used in hydration, now that i think about it. though, again, i suppose it is only a problem because i am handcrafting the initial model
Charles Rector
@chuckrector
Nov 07 2016 19:18 UTC
i should actually design a UX for this thing! rather than ad hoc experimentation. i'm sure no (good) UI for sprites ever had only a width input without the height, heh. you know, i think it's really easy for engineers to get into weird tunnel vision states due to being accustomed to executing on many small assigned tasks, versus exercising holistic bigger-picture reasoning skills that come with designing a full app and a coherent UX
Jean-Jacques Dubray
@jdubray
Nov 07 2016 19:33 UTC
yes, really understanding the work flow is key to a successful UX. I am not sure I understand what you are building, I thought you were just playing with the model / state representation and you needed a GUI to simulate actions. Are you trying to build some kind of design tools for games?
Charles Rector
@chuckrector
Nov 07 2016 19:36 UTC
no, you're correct -- it is playing around, but with the aim of exercising and understanding SAM better in practice. i wanted something more complicated than "foo bar baz" but not perhaps a full-blow application, just a nice in-between of medium complexity
as far as what it is exactly, i was thinking just a basic 2d scene compositor. like flash without animation. or maybe adding animation eventually
while it is easy to understand the basics of SAM on paper, it is a little more complicated in practice, so i want to keep going and building this out until i feel as though i have a decent grasp in practical terms of how to build things using SAM
well, i shouldn't say complicated in practice necessarily. nothing i have encountered has been complicated in terms of SAM. it's more in, i guess, firming up the "ratios & responsibilities". what things are better to go in actions, what things are better to live in the model logic, etc.
Jean-Jacques Dubray
@jdubray
Nov 07 2016 19:39 UTC
then I would clean up the relationship between action and model. I would definitely present a new sprite with height and width for instance. The model could still tweak these values, say with a default zoom ratio, that's the advantage of action/model, you would not want to action "add sprite" to know what's going on.
Charles Rector
@chuckrector
Nov 07 2016 19:39 UTC
the only complicated things i have done so far are struggling with text cursor! haha
Jean-Jacques Dubray
@jdubray
Nov 07 2016 19:46 UTC
ah ah, yes that one was a lot harder than it looked like
Charles Rector
@chuckrector
Nov 07 2016 19:56 UTC
the node/browser divide is an interesting one. i can include jquery in my main app.ts via imports and wire up various events with it and it's great. but if i wish to use jquery as part of my onClick events that are generated in the view, i must also include jquery in my index.html via script tag
i suppose alternatively instead of having all my views as string generators, they could generate actual elements
Jean-Jacques Dubray
@jdubray
Nov 07 2016 19:58 UTC
sorry you lost me a bit. Are you using Angular2? (app.ts), you would not need to use JQuery with ng2
Charles Rector
@chuckrector
Nov 07 2016 19:58 UTC
only typescript, no framework
TS is another exploration. so far it has been rather annoying and i put any everywhere. probably also due to not designing a lot up front
though in sublime it has been nice to see some of the errors it shows and the auto-completion
i am letting myself suffer through it to put pressure to eventually do that more thoughtful designing. but also because we will eventually use TS at my workplace, so it will help prepare me
i intended ES6 only at first, but TS/webpack/Sass quickly ballooned inside since they were so easy to configure. Sass is familiar, but TS & webpack are unfamiliar
Charles Rector
@chuckrector
Nov 07 2016 20:04 UTC
webpack is appealing to learn about because we have a complex handcrafted build process at my workplace
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:08 UTC

webpack is appealing to learn

never though I would see a sentence containing these words

Charles Rector
@chuckrector
Nov 07 2016 20:09 UTC
it seemed quite complex to me for a while. and various articles and the webpack docs themselves seemed to confirm that
however, if you read through the docs a bit and in the user comments, they direct you to an article/tutorial which describes what exactly webpack is and how it works and how to use it far better than the actual webpack documentation
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:10 UTC
you should take a look at ng2, it's quite easy to use with SAM
Charles Rector
@chuckrector
Nov 07 2016 20:10 UTC
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:11 UTC
the angular-cli hides all the webpack stuff.
Charles Rector
@chuckrector
Nov 07 2016 20:14 UTC
cool, i will check out ng2 then. i have not tried any angular at all yet. but many candidates for FE i have interviewed have experience with it, so it would probably be useful for me to get some familiarity
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:15 UTC
This is should give you a good idea (look at the dashboard section) https://github.com/jdubray/sam-samples/tree/master/angular2-admin-master-2.0.0
Charles Rector
@chuckrector
Nov 07 2016 20:16 UTC
that is one snazzy UI! using the much-touted material-ui looks like
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:23 UTC
I truly find Angular2 a pleasure to work with. It takes a bit of time to get all the pieces together at first, but once you have them, then TypeScript keeps it manageable. I would not have the same opinion without SAM, because they have a weird component programming model with parent/children and all the two-way databinding. SAM cuts through all that.
Charles Rector
@chuckrector
Nov 07 2016 20:24 UTC
did you learn angular2 mainly by trial and error?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:26 UTC
yes, it took me a couple of weeks to get comfortable with it. Can't claim I know everything, but once you get your project in place you feel that's really easy to add to it. I even figured out how to compile dynamic templates.
Fred Daoud
@foxdonut
Nov 07 2016 20:26 UTC
@jdubray I still wonder what ng2 truly brings to your table, since TypeScript is independent of ng2.
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:28 UTC
as I said, sometimes you just can't explain it, on paper ng2 would be everything I dislike, it's just once you try it, you feel it's pretty logical (minus two-way data binding and parent/child components).
Charles Rector
@chuckrector
Nov 07 2016 20:30 UTC
that is probably why i have not tried it yet -- two-way data binding. but i suppose it is not a commandment, merely a capability
i have watched some interesting talks (maybe linked in here?) where two-way data binding was described as a nightmare over time for complex real-world apps
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:33 UTC
yes exactly, I think everyone should stay away from two-way data binding
Fred Daoud
@foxdonut
Nov 07 2016 20:34 UTC
I guess it's because having a SAM implementation with Meiosis, all I need is a vdom library for the view, and ng2 is a whole ton of things I don't want.
meiosis-app-state.gif
I am really loving the state function!
V = f(S(M))
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:39 UTC
It's ok ! I am not trying to evangelize ng2! I understand your position.
devin ivy
@devinivy
Nov 07 2016 20:40 UTC
@foxdonut looks nice :)
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:40 UTC
I am glad you likethe state function!
Fred Daoud
@foxdonut
Nov 07 2016 20:40 UTC
thanks!
Since you said "on paper ng2 would be everything I dislike" then I understand, how perhaps "sometimes you just can't explain it" and you were able to make the best of it with ng2 and avoid the bad parts.
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:43 UTC
Computables in MobX would be a good paradigm for the State function
Fred Daoud
@foxdonut
Nov 07 2016 20:43 UTC
@devinivy glad you like it :) I released it this past weekend, I am working on updating the docs.
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:45 UTC
The State function shows how important it is to decouple the View from the Model. All this code would be hard to fit in the model or the view. It really keeps the view and the model simple, without making the state function that complex either.
Rick Medina
@rickmed
Nov 07 2016 20:45 UTC

@jdubray

Computables in MobX would be a good paradigm for the State function

That was the first thing I thought when looking at sam

Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:46 UTC
MobX is only missing the model, it has the exact same concept for actions, and then computables / observables map well to State / View
I don't know my Michel is not more open to it, it's not like it would break MobX.
Rick Medina
@rickmed
Nov 07 2016 20:46 UTC
@jdubray have you looked at this problem? https://github.com/slorber/scalable-frontend-with-elm-or-redux
devin ivy
@devinivy
Nov 07 2016 20:47 UTC
@jdubray earlier you spoke about some issues you had with defining boundaries (around the "state function"... in quotes because given the wiring it may not just be a function) with computables.
i think?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:47 UTC
I solved it afterwards
devin ivy
@devinivy
Nov 07 2016 20:48 UTC
nice!
do you mind elaborating a little bit?
on the whole i agree that computables are at least a good candidate for generating state from the model.
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:49 UTC
The wiring is never a big problem with SAM. The State function is a (true) pure function that takes the model as an input and return the state representation. It's irrelevant whether it is wired in a reactive loop or not.
devin ivy
@devinivy
Nov 07 2016 20:49 UTC
right! i see that
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:52 UTC
This is how I ended up doing it:
class State {
    @observable _counter = COUNTER_MAX;
    @observable _aborted = false;
    @observable _started = false;
    @observable _launched = false;

    public actions: Actions

    constructor( a: Actions) {
        this.actions = a ;
    }

    getActions() {
        return actions ;
    }

    representation (model) {
        console.log(model) ;
        this._counter = model.counter;
        this._started = model.started;
        this._aborted = model.aborted;
        this._launched = model.launched;

    }

    // Derive the current state of the system
    @computed get ready() {
        return ((this._counter === COUNTER_MAX) && !this._started && !this._launched && !this._aborted);
    }

    @computed get counting() {
        return ((this._counter <= COUNTER_MAX) && (this._counter >= 0) && this._started && !this._launched && !this._aborted) ;
    }

    @computed get launched() {
        return ((this._counter == 0) && this._started && this._launched && !this._aborted) ;
    }

    @computed get aborted() {
        return ( ( this._counter <= COUNTER_MAX) && (this._counter >= 0) 
            && this._started && !this._launched && this._aborted ) ;
    }



// Next action predicate, derives whether
// the system is in a (control) state where
// an action needs to be invoked

    nextAction() {
        if (this.counting) {
            if (this._counter>0) {
                actions.decrement({counter: this._counter}) ;
            }

            if (this._counter === 0) {
                actions.launch({}) ;
            }
        }
    }

    render(model) {
        this.representation(model)
        this.nextAction() ;
    }
}
The view is then wired as an observer to the state function
In the first instance, I had not used computables.
Fred Daoud
@foxdonut
Nov 07 2016 20:53 UTC
@rickmed are there any online demos of solutions?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 20:56 UTC
Remember it's a reactive system so you have to use computables when you update observables, you can't be naive about how you update your state representation.
In the sample I decoupled the update (of the model) from the reactive aspects of MobX, which IMHO is a lot cleaner than implementing the mutation with computables.
That way you don't have to worry about MobX reacting to an intermediate state
You can also wire nap() in the state function too
Rick Medina
@rickmed
Nov 07 2016 20:59 UTC
@foxdonut there are no "perfect" solutions. that is the problem :) the most acceptable solutions by now are middleware (sagas/redux-observable...)
Fred Daoud
@foxdonut
Nov 07 2016 21:00 UTC
@rickmed I think I could come up with a pretty clean solution with SAM/Meiosis :)
Jean-Jacques Dubray
@jdubray
Nov 07 2016 21:00 UTC
:+1:
Rick Medina
@rickmed
Nov 07 2016 21:01 UTC
@foxdonut :thumbsup: !
Fred Daoud
@foxdonut
Nov 07 2016 21:01 UTC
@rickmed I ask because I am not sure I fully understand the task, seeing it online (running app) would help!
Rick Medina
@rickmed
Nov 07 2016 21:07 UTC
@foxdonut read from "specification" don't get fed up in the details. The main problem is the point number 4
which tries to aim to an architecture which decouples the components (as to be "worked by different teams") with 1 team wiring the whole thing but in a fractal way
Jean-Jacques Dubray
@jdubray
Nov 07 2016 21:09 UTC
@rickmed looks like an interesting problem, thanks for sharing. #4 seems to be designed form SAM, but I have not read in detail.
Rick Medina
@rickmed
Nov 07 2016 21:12 UTC
you can imagine the newGif component like this: there is a search bar in which you type "cats" and it shows in the ui a card with a random gif (from eg giphy.com) related to cats. Everytime you search for a random gif, a new card is added to the ui
Rick Medina
@rickmed
Nov 07 2016 21:30 UTC
@jdubray do you mind elaborate on this "In the sample I decoupled the update (of the model) from the reactive aspects of MobX, which IMHO is a lot cleaner than implementing the mutation with computables."?
Fred Daoud
@foxdonut
Nov 07 2016 21:31 UTC
@rickmed what is "A pair of RandomGif" and "A pair of pair of RandomGif"? Why are they issuing actions like TOP_LEVEL_RANDOM_GIF_UPDATED and FIRST_RANDOM_GIF_UPDATED? Why can't RandomGif just be a single, reusable component and you can just put as many instances on the page as you want?
Rick Medina
@rickmed
Nov 07 2016 21:31 UTC
@foxdonut that is a fractal architecture
Fred Daoud
@foxdonut
Nov 07 2016 21:31 UTC
eh?
Rick Medina
@rickmed
Nov 07 2016 21:33 UTC
meaning, you build components the same way you build subcomponents and the whole system, like a lego house with the pieces being the same shape

"In fractal architectures, the whole can be naively packaged as a component to be used in some larger application.

In non-fractal architectures, the non-repeatable parts are said to be orchestrators over the parts that have hierarchical composition."

is about composition
each slider is a component, you can arbitrarily add instances to the page, each slider manages its own state, yet they are contained in the top-level, single root tree.
Rick Medina
@rickmed
Nov 07 2016 21:38 UTC
the question is, could I package let's say 2 sliders (that is just an example of delegating some part of the ui to a team), as a standalone app without changing the internals of the sliders? if not, then it is not fractal
btw, the overall problem at hand is a question to fractal architectures themselves (like redux or cycle js)
put this way: could I compose a component which is 2 sliders (composed by 2 separated slider componentes) to add 2 sliders at a time instead of one?
Fred Daoud
@foxdonut
Nov 07 2016 21:42 UTC
@rickmed I understand now.. thanks!
Jean-Jacques Dubray
@jdubray
Nov 07 2016 22:00 UTC
@rickmed the principles behind MobX is to use just observables and computables, but when you assign the value of an observable the computables recompute. SAM forces you to hide all the assignments until you are ready to create the state representation. I like that better, I don't like the model to "observable" like Redux's store or MobX. In the sample the State function is responsible for translating the model into observable values. Then all the magic can happen, and then nap().
The problem with RxJS is that you lose control of the mutation.
Edward Mulraney
@edmulraney
Nov 07 2016 22:28 UTC
"In theory you cannot start a new present method until the state function has completed."
@jdubray it makes sense then to ensure the loop can only run serially? lock the loop until its finished?
devin ivy
@devinivy
Nov 07 2016 22:29 UTC
usually that's trivial if state() is synchronous
Edward Mulraney
@edmulraney
Nov 07 2016 22:41 UTC
in js it will always be sync by default right?
devin ivy
@devinivy
Nov 07 2016 22:51 UTC
yes! when you see consecutively running lines of code in JS they always happen without pause (unless you are using await, which is in a future spec of JS). when you pass a callback, that's usually when async stuff can occur.
Jean-Jacques Dubray
@jdubray
Nov 07 2016 22:59 UTC
@edmulraney @devinivy yes, this constraint is not too hard to achieve in the browser. If you are doing an API call in the model you could run into trouble.
Edward Mulraney
@edmulraney
Nov 07 2016 23:01 UTC
thats what i was trying to work out - it works fine if its async in an action - but not in the model?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:02 UTC
yes ! that's the key, actions don't mutate anything! the mutation of the application state is in the model!
Rick Medina
@rickmed
Nov 07 2016 23:03 UTC
@jdubray would it be equivalent if you use transactions in mobx?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:03 UTC
ah? I don't know about transactions, is that a new concept?
Rick Medina
@rickmed
Nov 07 2016 23:03 UTC
or use actions (which are wrapped in transactions automatically)
Edward Mulraney
@edmulraney
Nov 07 2016 23:04 UTC
hmm actually, the model would still be in a stable state even if you had an async task in it.
it still has to follow: resolve in model -> mutate -> state()
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:05 UTC
it's just logically it's better to accept an action when the model has completed the mutation. It's not forbidden per say, but you could run into edge cases
Rick Medina
@rickmed
Nov 07 2016 23:05 UTC
there is also this concept of mobx strict, when enabled, only mutations to observables are permitted inside an "action" function, an action function is wrapped automatically in a transaction, a transaction lets you mutate the model several times and only let the computed/observers react when it is done
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:06 UTC
In general, when you do create/update/delete operations they need to be synchronized. The user would want to wait until the operation completes until a new action can be triggered.
Edward Mulraney
@edmulraney
Nov 07 2016 23:07 UTC
@jdubray i agree - i thought you were saying an api call in model could cause problems due to sync/async issues, but its not, its just about semantics
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:07 UTC
The model could also be partitioned to accept different action streams. The synchronization would happen at the stream level, rather than at the model level
yes, just semantics.
SAM is a very light weight structure, it's just here to help you reason about all these questions.
Edward Mulraney
@edmulraney
Nov 07 2016 23:08 UTC
V = S(vm(M.present(A(M))), nap(M, A))
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:08 UTC
Yep, that's it!
Edward Mulraney
@edmulraney
Nov 07 2016 23:08 UTC
does this appear on SAM.js or is this just what @schtauffen wrote on jackalope
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:09 UTC
I have talked about it that way in the article, I can't remember if I put it on sam.js
Edward Mulraney
@edmulraney
Nov 07 2016 23:09 UTC
cool
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:10 UTC
The paradigm shift introduced by Dr. Lamport is that an action can never decide of the end-state of the system.
Edward Mulraney
@edmulraney
Nov 07 2016 23:10 UTC
vm=view model?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:10 UTC
vm = view-model, yes
Edward Mulraney
@edmulraney
Nov 07 2016 23:10 UTC
but S =state which encompasses view-model
my state representation will have my properties that im interested in
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:11 UTC
it's hard to express everything as one simple formula! but yes, the idea is that the State function would compute / slide the properties of the view from the model
Edward Mulraney
@edmulraney
Nov 07 2016 23:11 UTC
V= nap(S(present(A(M)))
hmm not quite
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:12 UTC
no, nap does not produce a state representation
Edward Mulraney
@edmulraney
Nov 07 2016 23:12 UTC
ye
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:12 UTC
it triggers an action
Edward Mulraney
@edmulraney
Nov 07 2016 23:13 UTC
V=S(present(A(M)) nap must happen outside
on S
you need app state to determine nap
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:14 UTC
yes, it's like a call back to the State function
Edward Mulraney
@edmulraney
Nov 07 2016 23:14 UTC
sure
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:15 UTC
as I said, i't hard to represent everything in one formula, I just used it to show that it's not much more complicated.
Edward Mulraney
@edmulraney
Nov 07 2016 23:15 UTC
okay
we agree nap occurs on S?
it must do
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:16 UTC
yes, ideally after the state representation has been displayed by the view, but there could be some optimization when you know an action is going to be triggered and you don't display until the next-action completes.
It's a pattern, not an exact formula to follow. I really just want to provide some structure so that we don't rely on a library like React or RxJS to define the programming model.
Rick Medina
@rickmed
Nov 07 2016 23:33 UTC
if you want to represent SAM mathematically is not going to be that simple (at least for the non mathematician to understand), bc most of the functions (at least as I see it simply in the launcher example) are not pure (don't return anything). On the other hand, is cool that SAM is implemented with plain old functions, and is reactive, meaning, the whole thing is a recursive pattern, which you could represent mathematically but not like x = f(y)
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:36 UTC
please, do not mix up wiring with programming model.
This is a reactive wiring of the functions.
Edward Mulraney
@edmulraney
Nov 07 2016 23:39 UTC
in an enteprise, or a modular system, each component/feature would have it's own state and nap function
a new breed of bugs that gets introduced will be side effects from naps triggering steps triggering naps triggering steps....
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:40 UTC
state, actions and model are pure functions, unless you make an API call for instance
Rick Medina
@rickmed
Nov 07 2016 23:41 UTC
pure mathematical functions?
Edward Mulraney
@edmulraney
Nov 07 2016 23:42 UTC
from my understanding, it is still valid SAM if i make my state function return an object of StateRepresentation, then immediately after I call view.render(sr). its important to move this outside of the state function imo
each component has its own state-representation, but there only needs to be one render()
closter to V=render(S(present(A(M)))
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:46 UTC
@rickmed it does not matter if the result of the function is forwarded or not, for instance:
mySAMAction(event) {
       let proposal =  my_functional_action(event) ;
       present(proposal) 
}
This is just wiring, not material code
Rick Medina
@rickmed
Nov 07 2016 23:48 UTC
so you are saying that mySAMAction is a pure function?
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:49 UTC
@edmulraney I would disagree, each component has its prop, it is the state function which decide the shape of the state representation. You cannot have any business logic in the view. The view is pure component/display logic.
@rickmed yes, I do.
wiring does not influence whether or not it is a pure action.
Edward Mulraney
@edmulraney
Nov 07 2016 23:50 UTC
@jdubray agreed - view has no BL, render can be a function of the model or a subscriber to the model
sorry
render is wrong word
more like represent
split state and representing state. they need not be coupled and only introduces limitations
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:51 UTC
sure, I use display.
Edward Mulraney
@edmulraney
Nov 07 2016 23:51 UTC
V=represent(state(present(action(model))))
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:52 UTC
Yes, as I discussed with @foxdonut, it really depends on the Web framework you are using.
Edward Mulraney
@edmulraney
Nov 07 2016 23:52 UTC
okay great - just wanted to check for sure that its SAM compliant. i couldn't see why not. cheers
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:52 UTC
I have also introduced the concept of a "theme" to make the app more modular
You could replace the theme implementation and keep the view/state.
Rick Medina
@rickmed
Nov 07 2016 23:56 UTC
@jdubray a pure function needs to return a value (which is determined only by its inputs) without observable side effects
Edward Mulraney
@edmulraney
Nov 07 2016 23:57 UTC
yes, mySAMAction would be impure, regardless of its affect on model
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:58 UTC
It looks like we are not going to convince each other, for me the wiring of how you receive the output is independent of the fact that you are dealing with a pure function.
Edward Mulraney
@edmulraney
Nov 07 2016 23:58 UTC
pure function is just the wrong terminology for what you're trying to describe i think
Jean-Jacques Dubray
@jdubray
Nov 07 2016 23:59 UTC
I agree that some actions would be impure (say when they call an API)
Rick Medina
@rickmed
Nov 07 2016 23:59 UTC
I hate arguing just for the sake of it, but the problem I see is that if we don't use universal semantics it will confuse a lot of people and be detrimental in the adoption of sam
Edward Mulraney
@edmulraney
Nov 07 2016 23:59 UTC
ah i see - yes
@rickmed agreed