• bleistift2@sopuli.xyz
    link
    fedilink
    English
    arrow-up
    96
    ·
    10 hours ago

    One way in which this could have come about is that Math.random wasn’t supported in all relevant browsers when the library author wrote the library. So they had to roll their own randomness with blackjack and hookers. Later the web standards evolved and the author was able to remove the custom code, but now had people relying on his library’s exposing a getRandom function.

    • Billygoat@piefed.social
      link
      fedilink
      English
      arrow-up
      25
      ·
      5 hours ago

      You see this kind of stuff in C all the time when a code base supports multiple OSs by using macros.

      • chonglibloodsport@lemmy.world
        link
        fedilink
        arrow-up
        15
        ·
        5 hours ago

        Yes, though at least with C you have the compiler to optimize the cruft out of your binary and end up with a nice, clean program.

        With JavaScript this is going to incur some runtime cost everywhere this library is used, even if it only happens once when getting optimized out by the JIT compiler.

  • Avicenna@programming.dev
    link
    fedilink
    arrow-up
    16
    ·
    9 hours ago

    I can imagine multiple scenarios where this could be useful. Simplest is perhaps the coder imagined at the time they could extend the function in later stages.

        • bleistift2@sopuli.xyz
          link
          fedilink
          English
          arrow-up
          9
          ·
          5 hours ago

          “You ain’t gonna need it”. It means: Build the thing you need now and don’t try to predict what you’ll need in 3 years. You ain’t gonna need it anyway.

      • Avicenna@programming.dev
        link
        fedilink
        arrow-up
        7
        ·
        edit-2
        7 hours ago

        I think it is a balance. Despite having quite functional IDEs now a days, it is still more error prone to change 10 instances of math.random than a single function you define modularly. If you think there is a good chance such an extension might be needed in future or that you might want to change libraries later on, I wouldn’t necessarily call this a bad decision, even if it goes unused.

        YAGNI works best when it prevents adding complex unused futures which are error prone and complicates a simpler program logic and flow. In this case you are just encapsulating a function inside another one without any change to program complexity.

        • Jesus_666@lemmy.world
          link
          fedilink
          arrow-up
          2
          ·
          6 hours ago

          It definitely depends on the use case. I could accept this being abstracted out to facilitate mocking, for instance (although I’d recommend mocking at a higher level). But in general this wouldn’t pass review with me unless I get a good explanation for why it’s necessary.

  • rtxn@lemmy.world
    link
    fedilink
    arrow-up
    60
    ·
    edit-2
    12 hours ago

    Considering how many websites were temporarily obliterated by the left-pad fiasco, being an npmjs maintainer might be an even higher power-to-effort ratio (by virtue of a near-zero denominator) than being a billionaire CEO.

  • katy ✨@piefed.blahaj.zone
    link
    fedilink
    English
    arrow-up
    33
    arrow-down
    1
    ·
    12 hours ago

    i mean its still good to use an abstraction layer in case you ever have to change the underlying call; it’s far easier to change it in one place instead of replacing every call

    • Blue_Morpho@lemmy.world
      link
      fedilink
      arrow-up
      13
      ·
      9 hours ago

      Is this a joke?

      If you need a different random function, you write a different random function either way. Having one function do nothing but call another function does nothing.

      • loutr@sh.itjust.works
        link
        fedilink
        arrow-up
        22
        ·
        9 hours ago

        There are several legit reasons why you’d do this. Unit tests, for example: override getRandom() with an implementation that always returns the same series of numbers, and now you have repeatable tests without touching the production code.

        • WhiteRice@lemmy.ml
          link
          fedilink
          arrow-up
          2
          ·
          edit-2
          9 hours ago

          Can you override Math.random within a local scope?

          At my shop we do create generic covers for vendor specific functionality, for the reasons you stated. Though the practice was started in case we ever needed to swap vendors.

          • bleistift2@sopuli.xyz
            link
            fedilink
            English
            arrow-up
            6
            ·
            5 hours ago

            You can, but you shouldn’t. You don’t know what else relies on Math.random. That’s why there’s the wrapper function. That you can override in unit tests without worrying about other, unrelated code.

      • x1gma@lemmy.world
        link
        fedilink
        arrow-up
        4
        ·
        8 hours ago

        It’s not about a different function providing different randomness, but providing a compatible implementation for environments not supporting the “regular” implementation.

        If this screenshot is legit, I guarantee you that either the library is older and there was some weird branching for IE or it’s brand new and had branching for the hot new JS runtime / cross compiling.

        Supporting a metric fuckton of browsers and environments takes the same amount of shims.

  • abbadon420@sh.itjust.works
    link
    fedilink
    arrow-up
    14
    ·
    edit-2
    12 hours ago

    I get that this is a joke, but would something like import Math.random as getRandom work better? Because that basically what you’re doing here, renaming the function.

    • sunnie@sopuli.xyz
      link
      fedilink
      arrow-up
      17
      ·
      12 hours ago

      that’s not really a thing in JS as Math isn’t imported, it’s just an object available globally. the closest you can get is like const { random: getRandom } = Mathbut that’s just uglier.

      the implication is that this function is exported from a library so they have to keep the function around - obviously in a modern project you’d just do Math.random()

        • bleistift2@sopuli.xyz
          link
          fedilink
          English
          arrow-up
          4
          ·
          10 hours ago

          Which of these things, excactly? That Math just floats around in the global scope? Or that that destructuring assignment works? Or that the author chose to abstract around Math.random(). That has come very handy for me when testing.

          • MonkderVierte@lemmy.zip
            link
            fedilink
            arrow-up
            6
            ·
            edit-2
            10 hours ago

            Global scope.

            Also that there’s, after 35 years of webbrowsers, still no reliable way to match a domain without subdomain, except via string splitting.

            • bleistift2@sopuli.xyz
              link
              fedilink
              English
              arrow-up
              4
              ·
              10 hours ago

              I see your point regarding global scope. But personally, I’ve never encountered an issue with it. And it’s kinda nice not to have to import fetch every time you need it.

              Regarding subdomains, if you’ll humor my curiosity: What’s the use case? I also wonder what an API for this might look like.

              const {domain, subdomains, rootDomain} = new URL('https://wikipedia.org/')
              //       'wikipedia.org', [], 'wikipedia.org'
              const {domain, subdomains, rootDomain} = new URL('https://foo.bar.baz.net/')
              //       'foo.bar.baz.net', ['foo.bar.baz.net', 'bar.baz.net'], 'baz.net'
              
              • MonkderVierte@lemmy.zip
                link
                fedilink
                arrow-up
                3
                ·
                edit-2
                8 hours ago

                A userscript over links for debugging purposes, that should ignore links to the same domain (www.w3schools vs campus.w3schools vs www.youtube).

                Btw, it’s funny how support.mozilla and some standards-teaching-sites are some of the worst offenders of web standards.

                Edit: how to stop this auto-linking?

                • bleistift2@sopuli.xyz
                  link
                  fedilink
                  English
                  arrow-up
                  2
                  ·
                  9 hours ago

                  If I understand correctly, you want to check the current domain (eg. w3schools) against api.w3schools and www.youtube, and return true for the first and false for the second (or the other way around)

                  Then technically it’s possible without string splitting:

                  const href = 'retrieved-from-the-anchor-element';
                  (new URL(href, location.href).hostname).endsWith(location.hostname)
                  
            • dreamkeeper@literature.cafe
              link
              fedilink
              arrow-up
              2
              ·
              edit-2
              9 hours ago

              Most scripting languages have a global scope of some kind. It’s not that big of a deal.

              I’d prefer not to have it, but it hasn’t caused me problems in many years. Actually, the window object can be really useful in some situations so I’m not even sure about that.

    • Dæmon S.@calckey.world
      link
      fedilink
      arrow-up
      1
      arrow-down
      2
      ·
      11 hours ago

      @abbadon420@sh.itjust.works @not_IO@lemmy.blahaj.zone @programmer_humor@programming.dev

      I didn’t know about this specific syntax you mentioned (import foo.bar as baz; what I’m aware and I use frequently is something like e.g. const log = console.log.bind(console)), I’m not even sure if it works as all my import use cases involve something installed from NPM or a relative-folder module file.

      But sometimes it’s useful, and better, to have parametrized randomness, as in my helper functions I keep reusing across my personal projects:

      export const rand = (n, x) => Math.random() * (x - n) + n
      export const irand = (n, x) => Math.round(rand(n, x))
      export const choose = a => a[irand(0, a.length-1)]
      

      (yeah, my choose helper lacks a proper verification of the input parameter, will return undefined if the array is empty or is not an array, but, well, it’s for personal projects so I don’t really worry about that; also I’ve been using Ruby more than I use JS, and Ruby got beautifully native array.sample and Random.rand(a..b))

      • bleistift2@sopuli.xyz
        link
        fedilink
        English
        arrow-up
        2
        arrow-down
        1
        ·
        edit-2
        10 hours ago

        I think irand should floor instead of round. First, when you want to generate a random number between 0 and 6 it’s often useful to exclude the 6. (See also https://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD831.html).

        Second, irand(0, 6) has a higher chance of rolling a 1 than a 0 because the capture area for 1 is [.5, 1.5), while the capture area for 0 is only [0, .5)

        Also if you had chosen any other variable names, it wouldn’t have taken me 5 minutes to figure out what these things do.

        And one more, if you had swapped the input parameters, and defaulted to n=0, you could call the use case “random number up to 6” with just rand(6) instead of rand(0, 6).

        None of that matters in a private project, but… it just itched to point these things out.

        • Dæmon S.@calckey.world
          link
          fedilink
          arrow-up
          0
          arrow-down
          1
          ·
          9 hours ago

          @bleistift2@sopuli.xyz

          Back when I coded that (it’s been years), I opted for the shortest one-liners possible (I often catch myself doing one-liners and code-golfing for the fun of it), with “n” and “x” meaning respectively “miN” and “maX”. Hence why I also do a call to rand inside the irand, so irand is as shortest as possible.

          As for the bias towards the mid, it’s by design, because ends up quite similar to the central limit theorem:

          > Array.from(Array(10000), _=>irand(0,6)).reduce((p,v)=>({...p, [v]: (p[v]||0)+1}), {})
          {
            '0': 840,
            '1': 1602,
            '2': 1658,
            '3': 1691,
            '4': 1684,
            '5': 1687,
            '6': 838
          }
          

          Notice how both extremities have lower values while the median (3) got the maximum value (1691). I wanted something behaving similarly to how noise often feels like in real world settings (e.g. SDR radio settings, never truly uniform), hence why the “maX” value is inclusive, it’s purposefully meant to, just like the “miN” value is also inclusive, because they need to be inclusive so it gets to appear amidst the samples.

          As a comparison, when I change it to trunc (because floor would behave differently for negative numbers), as in:

          > irand = (n, x) => Math.trunc(rand(n, x))
          [Function: irand]
          > Array.from(Array(10000), _=>irand(0,6)).reduce((p,v)=>({...p, [v]: (p[v]||0)+1}), {})
          { '0': 1684, '1': 1659, '2': 1685, '3': 1668, '4': 1676, '5': 1628 }
          

          …then the sample gets too annoyingly uniform, not even to say about how max (the 6) ends up missing from the samples (thus requiring a x+1 whenever the max value is intended to be inclusive). This may be the best distribution for certain scenarios where uniform randomness is expected, but this doesn’t feel… natural.

          That’s also why I implemented this JS flavour in a personal Ruby gem (utils.rb) because Ruby got this annoyingly uniform distribution with its native Random.rand (again, useful sometimes, but not exactly natural):

          irb(main):041:0> 10000.times.map{Random.rand(0..6)}.tally.sort{|a,b|a[0]<=>b[0]}.to_h
          => {0=>1431, 1=>1449, 2=>1395, 3=>1435, 4=>1411, 5=>1465, 6=>1414}
          irb(main):042:0> def rand(n,x) = Kernel::rand()*(x-n)+n
          => :rand
          irb(main):043:0> def irand(n,x) = rand(n,x).round
          => :irand
          irb(main):044:0> 10000.times.map{irand(0,6)}.tally.sort{|a,b|a[0]<=>b[0]}.to_h
          => {0=>892, 1=>1612, 2=>1744, 3=>1643, 4=>1592, 5=>1708, 6=>809}
          

          See how my Ruby’s irand implementation behaves exactly as my JS’s irand do: with this more natural bias towards the middle.

          As for the possibility to do irand(x), because the use case often involves having a well-defined range instead of a maximum target value (minimum value isn’t even always zero, but something arbitrary such as e.g. irand(65,65+25) for generating codepoints for alphabet letters), this is why it’s not overloaded so to default n to zero.

          • bleistift2@sopuli.xyz
            link
            fedilink
            English
            arrow-up
            2
            ·
            9 hours ago

            Thanks for the write-up. Your use case is different from all I’ve ever had. It’s good to be reminded that my world view isn’t universal.