1. 3

    Thanks for this post. Although I consider paradigms to be just tools, for the past three years I often had to explain to others in the team why I am not “writing the real code” (which for them was always OOP) and choose functional approach instead. If I’m forced to work with such people in the future again, I will just point them to your excellent blog post.

    1. 4

      One function I found useful to make smooth but snappy slider UIs and such is “detent”

      function detent(n, detent, grid, snap) {
        // This function is useful to implement smooth transitions and snapping.
        // Map all values that are within detent/2 of any multiple of grid to
        // that multiple. Otherwise, if snap is true, return self, meaning that
        // the values in the dead zone will never be returned. If snap is
        // false, then expand the range between dead zone so that it covers the
        // range between multiples of the grid, and scale the value by that
        // factor.
        var r1 = roundTo(n, grid); // Nearest multiple of grid
        if (Math.abs(n - r1) < detent / 2) return r1; // Snap to that multiple...
        if (snap) return n // ...and return n
        // or compute nearest end of dead zone
        var r2 = n < r1 ? r1 - (detent / 2) : r1 + (detent / 2);
        // and scale values between dead zones to fill range between multiples
        return r1 + ((n - r2) * grid / (grid - detent));
      }
      

      (that’s from lively.lang, MIT licensed)

      Here is an example of a free-form rotation that still allows stickiness at 45 degree angles: https://twitter.com/robertkrahn/status/1403102487717023746 The gif doesn’t really transport it but it just feels “smooth”.

      I’ve first seen it in Squeak/Smalltalk where it was added by Dan Ingalls in 1998 but don’t know what the history behind it is. It might have been part of earlier Smalltalks. For completeness, here is the Smalltalk version:

      detentBy: detent atMultiplesOf: grid snap: snap
         "Map all values that are within detent/2 of any multiple of grid to that multiple.  Otherwise, if snap is true, return self, meaning that the values in the dead zone will never be returned.  If snap is false, then expand the range between dead zones so that it covers the range between multiples of the grid, and scale the value by that factor."
         | r1 r2 |
         r1 := self roundTo: grid.  "Nearest multiple of grid"
         (self roundTo: detent) = r1 ifTrue: [^ r1].  "Snap to that multiple..."
         snap ifTrue: [^ self].  "...or return self"
      
         r2 := self < r1  "Nearest end of dead zone"
             ifTrue: [r1 - (detent asFloat/2)]
             ifFalse: [r1 + (detent asFloat/2)].
         "Scale values between dead zones to fill range between multiples"
         ^ r1 + ((self - r2) * grid asFloat / (grid - detent))
      

      (that version is from Squeak 5.3, MIT licensed with the changeset indicating di 2/19/98 21:58 as the author).

      1. 1

        Could you provide some more information about detent? I have tried to find something, but without any luck

        1. 4

          In essence it’s just a way to provide a free-form choice of values in a certain range while also allow snapping to certain multiples /without/ disallowing certain values (e.g. those that are close to multiples).

          Here are two plots:

          (and sorry for the links to twitter but I dunno how to share images otherwise)

      1. 1

        Very nice summary. I wish candidates read it before taking code assignment/being interviewed. We are hiring right now and for some people “Array extras” are still uncharted territory.

        1. 7

          I’ve worked with a lot of great JS developers who still use MDN / google to look up basic array methods, maybe this isn’t the best way to evaluate candidates?

          1. 6

            Hell, I worked on a JS engine for most of a decade, and was literally on TC39, and still have to check the docs for argument orders and what not.

        1. 1

          Interesting read, but I don’t get the point about database being a single source of truth. It is for sure valid when talking about backend. But in frontend, the state is more than just the data. The store stores things like which menu item is selected (author’s own example), which view is currently loaded, etc. Among these, there will be data fetched from the database, like some information about the user. But this is just a part of the whole store.

          1. 2

            I like the use of some smart CSS tricks here, but I really think that using most of those effects would result in harming the usability (e.g. sometimes it is not obvious that an element is a button). Also, some of the buttons are a bit misaligned in Firefox (Developer Edition, version 81.0b3)