[HN Gopher] Fixing a performance problem in Elm using Html.Lazy
       ___________________________________________________________________
        
       Fixing a performance problem in Elm using Html.Lazy
        
       Author : ingve
       Score  : 64 points
       Date   : 2021-12-13 13:37 UTC (9 hours ago)
        
 (HTM) web link (blogg.bekk.no)
 (TXT) w3m dump (blogg.bekk.no)
        
       | matsemann wrote:
       | It's a bit pain to update nested types in elm, that's why I've
       | often ended up having data that can change as flat as possible.
       | But that also means that a too big a model is passed around
       | between functions I guess, because it's annoying having to
       | extract and pass the 3-4 values it needs.
       | 
       | What would happen if the view functions in elm by default were
       | lazy? Since everything in elm is immutable, and the functions
       | can't have side effects, it shouldn't be able to break anything.
       | I guess the problem is that this kind of memoization can be
       | memory heavy? But it doesn't really have to memorize everything.
       | Just the last inputs, and then do nothing with the DOM if the
       | inputs are the same. And the inputs are all derived from the main
       | model, which is only ever updated one place in the runtime. So
       | just keep track of changes when that happens, and what functions
       | that affect, hmm.
        
         | yakshaving_jgt wrote:
         | I've found lenses to be a reasonable way around this issue, so
         | I'm not too uncomfortable with nesting records. If you're
         | interested, I wrote a bit about that here[0].
         | 
         | [0]: https://jezenthomas.com/how-i-write-elm-applications/
        
         | _greim_ wrote:
         | > What would happen if the view functions in elm by default
         | were lazy?
         | 
         | `lazy` incurs a performance penalty not in memory but in
         | execution steps, due to having to do a deep-equality comparison
         | on the prev/next view inputs in the underlying JS. Enabling
         | such a check globally would, on balance, slow everything down.
         | Only if the work of running the view exceeds that small
         | penalty, would you want to use it strategically here and there,
         | hence the reason it's opt-in.
         | 
         | Even if the Records and Tuples proposal[1] lands and Elm gets
         | refactored to use it internally, thus allowing `===`
         | comparisons in the underlying JS instead of deep-equality, it's
         | unclear whether enabling it globally would be good or bad,
         | simply because the JS engine itself would presumably still be
         | doing some kind of deep-equality check behind that innocuous-
         | looking `===` operator. When the time comes I'll be curious to
         | see if there's any progress on that front.
         | 
         | [1] https://github.com/tc39/proposal-record-tuple
        
           | Skinney wrote:
           | It's a shallow comparison, not deep.
           | 
           | But yes, there is both computational and memory overhead when
           | using Html.Lazy
        
             | matsemann wrote:
             | But isn't there also that when _not_ using lazy? Instead of
             | checking model equality, elm runtime now has to check
             | shadow dom equality and calculate dom operations to apply.
             | My thought is that my idea isn 't obviously worse.
        
               | Skinney wrote:
               | Yes, but lazy adds to that work. So for views that will
               | pretty much always need to be re-rendered (because the
               | only changes are at the bottom of the view hierarchy)
               | then wrapping them in lazy will only slow down the dom
               | recalculation.
        
             | _greim_ wrote:
             | > It's a shallow comparison, not deep.
             | 
             | Interesting. I thought it was doing a deep comparison, but
             | apparently it uses reference equality, and thus shallow.
        
         | Kaze404 wrote:
         | > a too big a model is passed around between functions I guess,
         | because it's annoying having to extract and pass the 3-4 values
         | it needs
         | 
         | You can always take an extensible record type instead of the
         | entire model.                 type alias Model =           {
         | count : Int           , prop : String}            initialModel
         | : Model       initialModel =           { count = 0           ,
         | prop = "" }            foo : { a | prop: String } -> String
         | foo model = model.prop            bar = foo initialModel
        
       | vijaybritto wrote:
       | From what I understand this is the same as memo() in React? Also
       | the splitting up models to only send data that is needed for the
       | view sounds like matchStateToProps when using Redux. Funny how
       | the problems in browsers make us solve the same problems in
       | different ways and we end up feeling mind blown every time!!
        
       | truculent wrote:
       | Nice post! I enjoyed it.
       | 
       | I don't understand why Html.Lazy uses referential equality
       | instead of equality. In a language like Elm shouldn't they be the
       | same? What potential bugs would using equality introduce?
       | 
       | Thanks
        
         | masklinn wrote:
         | From what I understand it's Html _.Styled_.Lazy (from elm-css)
         | which uses referential equality, possibly because it defines
         | its own vdom in order to integrate styling information.
        
           | truculent wrote:
           | I think it is both.
           | 
           | In the process of confirming, I think I found the answer
           | within the Elm docs[0]:
           | 
           | > Note: When are two values "the same" though? To optimize
           | for performance, we use JavaScript's === operator behind the
           | scenes
           | 
           | > ...
           | 
           | > Using reference equality is always cheap O(1), even when
           | the data structure has thousands or millions of entries. So
           | this is mostly about making sure that using lazy will never
           | slow your code down a bunch by accident. All the checks are
           | super cheap!
           | 
           | It's not as elegant but you'd only ever use Html.Lazy for
           | performance, so this trade-off seems to make perfect sense.
           | 
           | [0]: https://guide.elm-lang.org/optimization/lazy.html
        
           | Skinney wrote:
           | Author here. It's both. Html.Styled is converted to regular
           | Html when run, meaning it uses <<normal>> Html.Lazy under the
           | hood.
        
       ___________________________________________________________________
       (page generated 2021-12-13 23:01 UTC)