These are chat archives for jdubray/sam

6th
Aug 2017
Janne Siera
@jannesiera
Aug 06 2017 15:23
@jdubray when going framework-less, how do you perform DOM updates? Do you always just replace everything in the dom? Or do you use some diff/patching algorithm? Or something else?
Jean-Jacques Dubray
@jdubray
Aug 06 2017 15:34
@jannesiera yes, exactly, it works just fine in 99% of the cases. With WebAssembly coming it can only work better.
I do some basic check whether I need to replace say header/nav, page section or footer
I have also tried to compute a simple hash to see if different parts of the page have changed (I recompute them all the time), and only render the ones that changed.
When you replace a big chunk of the page that didn't change, you would see it flicker (say master / detail). The hash solves that problem.
Jean-Jacques Dubray
@jdubray
Aug 06 2017 15:40
I am just trying to illustrate that:
a/ functional HTML is great
b/ you don't always need a diff library, but it doesn't hurt to use one.
@sladiri IMHO, "effects as data" (or even NAP()) is more complex than making the model synchronized (i.e. a critical section that cannot be executed concurrently). Forcing the model to be a pure function, just because you don't have to deal with concurrency issues is not desirable.
Janne Siera
@jannesiera
Aug 06 2017 16:02
@jdubray interesting. How do you deal with stuff like input fields?
@jdubray and cleaning up event listeners and stuff?
Jean-Jacques Dubray
@jdubray
Aug 06 2017 16:18
I don't need to clean up event listeners because the HTML is generated with event listeners (onclick, ...). After every step I have a consistent view of the front-end.
Input fields are generated, I can also populate them with the values in the model (as part of the HTML generation) when necessary (say when there is an error in the form and you don't want to lose all the data).
I can even deal with modals very easily!
All this is possible because of HTML5, I think it would be much harder to use functional HTML prior to HTML5
Janne Siera
@jannesiera
Aug 06 2017 16:20
@jdubray but don't you lose input values on re-render? Or do you store all the values in the model, always?
Jean-Jacques Dubray
@jdubray
Aug 06 2017 16:20
It's nearly 100% declarative.
I don't lose anything because they are stored in the model after say a submit event
The only tricky thing I had to deal with is a search field with automatic search (say after 3 characters). But I think React had the same issue, not sure if it's better now.
Janne Siera
@jannesiera
Aug 06 2017 16:21
sure, but what with a change event on the input?
Jean-Jacques Dubray
@jdubray
Aug 06 2017 16:22
?
Janne Siera
@jannesiera
Aug 06 2017 16:22
you re-render the whole page everytime a user types a letter in an input field?
Jean-Jacques Dubray
@jdubray
Aug 06 2017 16:23
You cannot do that!
Janne Siera
@jannesiera
Aug 06 2017 16:23
Sorry?
Jean-Jacques Dubray
@jdubray
Aug 06 2017 16:26
This example shows how to retain the focus on a search field:
filter action:
filter: (data: any, present?: (data: any) => void) => {
            present = present || _present ;
            let _data : any = {startWith: data.name} ;
            if (data.focus) {
                _data.focus = data.focus ;
            }
            // next step of the reactive loop: present values to the model        
            present(_data) ;
            return false ;
        },
corresponding input:
header: (startWith: string, intents: any) => { 
            const filterValue = (startWith) ? ` value="${startWith}"` : ``
            if (startWith) {
                return '' ;
            } else {
                return `<h1>App DEV COE Core Team List</h1>
                        <input  id="search" placeholder="Search..." autofocus autoGrow
                                #searchInput ${filterValue} (input)="${intents['search']}'id':'search','name':searchInput.value})"><br>` ;
            }
        }
and of course the innevitable NAP:
var _nextAction = (model: any) => {

        if (model.focus) {
            console.log('refocusing query field '+ model.focus) ;
            setTimeout(function(){
                var el: any = document.getElementById(model.focus) ;
                if (el) {
                    el.focus();
                    var val = el.value; //store the value of the element
                    el.value = ''; //clear the value of the element
                    el.value = val; //set that value back.  
                }
            }, 20);
        }

    } ;
Little bit of DOM manipulation here, can't avoid it, because in that particular case HTML5 is not declarative, but there are very few cases like that.
You see more cases with fancy JQuery components (C3, DataTables...) but it's the same principle, you generate the HTML and you call the corresponding API in the NAP()
Janne Siera
@jannesiera
Aug 06 2017 16:32

You cannot do that!

Could you clarify what you mean by this? What can I not do, and why?

Jean-Jacques Dubray
@jdubray
Aug 06 2017 16:33
You cannot rerender the whole page for every character!
In that particular sample, I render just the header
Janne Siera
@jannesiera
Aug 06 2017 16:33
Then how would you handle e.g. a confirm password field in a form, that gives 'live' feedback about whether your passwords match as you type.
Jean-Jacques Dubray
@jdubray
Aug 06 2017 16:37
same way?
You don't have to render all the time, you can only render on a change of UI, the action can decide that for instance. In that case it will not present any proposal.
Jean-Jacques Dubray
@jdubray
Aug 06 2017 16:53
In part 2 of Dr. Lamport's talk he explained that he realized that the only way to describe safety conditions was to use "state machines" (again, his definition does not align with Petri Nets, i.e. classical state machines).
Jean-Jacques Dubray
@jdubray
Aug 06 2017 17:01
He explains a bit later that his contribution to the Temporal Logic of Action was the "next-state-formula" (in the case of SAM, the model), i.e. where he could explicitely relate S1 with S2, but not as an edge relating two nodes, but as a formula/step taking S1 as an input and computing the next-state (S2). It's a bit innacurate to use S1 and S2 because they only represent a snapshot of the model property values, not a node in the Petri Nets.
IMHO, that's how we should program, ignoring the true nature of state/step is too error prone.
Jean-Jacques Dubray
@jdubray
Aug 06 2017 17:08
This where he describes proposals: https://youtu.be/uK9yGNuGWKE?t=2708
Janne Siera
@jannesiera
Aug 06 2017 18:00

@jdubray Sorry, that might not have been the best example. Let's say you have a textarea and a counter of how many characters you have typed already. We have some of these to see how close you are to a certain max number of characters.

In this case you would have to rerender on every 'letter typed'. Would you store the value in the model on every change, and then re-render the whole page?

Slađan Ristić
@sladiri
Aug 06 2017 18:30
@jannesiera What about:
  • you could update the displayed value (state-representation) in steps, e.g. only every ten characters, so there would be fewer DOM updates
  • you could circumvent the SAM loop by using a stateful component for the intermediate UI updates, if you do not care about the exact value being in the model
Antanas A.
@antanas-arvasevicius
Aug 06 2017 18:35
but how about if there is a need for more complex GUI
e.g. modal windows
proper keyboard focus manager
handling context menus
Jean-Jacques Dubray
@jdubray
Aug 06 2017 19:09
In these cases you call always encapsulate the DOM manipulation in say a function, for instance that sample (fishing game): https://github.com/jdubray/sam-samples/blob/master/js-rectangle/rectangle.html
the "draw" function does not return HTML (as it would normally do), it returns a function that is added to the state representation:
function draw(x,y, w,h,c) {
    var x0 = x,
        y0 = y,
        w0 = w,
        h0 = h,
        c0 = c || "#000000" ;

    return function(ctx,canvas) {
      ctx.clearRect(0,0,canvas.width,canvas.height);
      ctx.fillStyle = c0  ; 
      ctx.fillRect(x0,y0,w0,h0);
    }
}
The theme calls that function (which draws the fishing net in the canvas):
net(net, fishes) {
         // draw the net
        *** net.rect(this.ctx,this.canvas) ;   ***
        if (fishes>0) {
                var _fishes = (fishes>1)? ' fishes!' : ' fish' ;
                net.caught.innerHTML = " you caught "+fishes+_fishes ;
            } else {
                net.caught.innerHTML = "" ;
        }
    },
I don't think that's horrific, it fits well within the SAM pattern, and as I mentioned HTML5 is not 10)% declarative, but pretty close.
HTML5 modals work very well, there is no issue at all.
Jean-Jacques Dubray
@jdubray
Aug 06 2017 19:14
Even react does direct DOM manipulations (e.g. spinner)
It's not a problem of the pattern, that's a problem of HTML5

@jannesiera you can always reduce the rendering surface to avoid the input field. There is no problem at all. The example I showed you was with dynamic Angular 2 templates and I was trying to render the whole thing.
Of course it's not a good idea to fragment your rendering surface too much (just hard to code) but to be frank when you use a stateRepresentation like:

{
     div1: "<p>....</p>",
     div2: "..."
      ...
}

and you are smart about what renders, either with hashes or with fragments, you don't have this kind of issue. The issue with input fields only comes when you need to render the input field that's being keyed in

Jean-Jacques Dubray
@jdubray
Aug 06 2017 19:24
Dr Lamport on the benefits of TLA+: https://youtu.be/uK9yGNuGWKE?t=4991
"Programming languages do not have better abstractions, they simply allow you to hide information"
Slađan Ristić
@sladiri
Aug 06 2017 19:54
Yes, I found that nice, that the goal of TLA is not to hide things behind abstractions, but to show them in a clear way. He did say that he has not used TLA specs often in his daily work, but that he always writes a clear description of a method, at least in English.
Jean-Jacques Dubray
@jdubray
Aug 06 2017 21:29
:+1: