• ☆ Yσɠƚԋσʂ ☆OP
    link
    fedilink
    arrow-up
    7
    arrow-down
    11
    ·
    8 months ago

    The idea is to get the current data that will have the current year, month, day in it, and then to query this date for the previous month. A sane API would just throw an error when the date is out of range. A Js API will quitely give you nonsense instead. Again, side effects have absolutely nothing to do with anything here.

    • aebletrae [she/her]@hexbear.net
      link
      fedilink
      English
      arrow-up
      7
      arrow-down
      1
      ·
      8 months ago

      You’ve replied while I was editing, so see that regarding what I mean by side effects.

      As far as throwing an error when you try to create “31st February”, this wouldn’t actually help much, since the error would still only occur on some days of the year, because your original code doesn’t account for the range of outputs from Date() when called without arguments.

      To perform correctly, your code needs to normalise the day of the month, or just create the date more explicitly to begin with, but this is a calendrical issue, not a JavaScript one.

      • ☆ Yσɠƚԋσʂ ☆OP
        link
        fedilink
        arrow-up
        7
        arrow-down
        12
        ·
        8 months ago

        Side effects are when your function has a reference to some state outside its scope and modifies that state. A function that produces different outputs when it’s called, such as getting a current time is not an example of a side effect. Again, the issue here is that Js tries to infer what to do with a bad input, a number outside acceptable range, instead of simply rejecting it.

        My point isn’t that you can’t write a better function that’s less error prone, but the fact that Js allows such things to happen in the first place. It’s a very easily avoidable problem at the API level.

        • aebletrae [she/her]@hexbear.net
          link
          fedilink
          English
          arrow-up
          7
          arrow-down
          2
          ·
          8 months ago

          I was taught that side effects are not so one-sided, and that changing output in response to outside state (such as the date) is also a side effect, a side effect on the function, rather than a side effect of the function, but I’m happy to use other definitions so long as they’re commonly understood.

          As I said before, though, even if JavaScript did throw an error as you’d prefer, it would still allow your function to have date-based problems. They’d be a bit noisier perhaps but no less present, and just as “well it’s worked fine so far”. And that’s because, as I keep saying, the real problem here is using a function with inconsistent output and not thoroughly dealing with the possibilities. An API change wouldn’t alter that. Most of the time it would still let you write bad code.

          I also probably agree with you that errors are generally better than silence in response to bad input but, as someone else has said (more or less) it’s not always unreasonable to consider “31st [Month]” as 31 days after the end of [Previous Month]. Without throwing errors, this flexibility is possible. Perhaps the creators believed having to mutate the day-of-month first was an acceptable trade-off for that.

          • ☆ Yσɠƚԋσʂ ☆OP
            link
            fedilink
            arrow-up
            6
            arrow-down
            9
            ·
            8 months ago

            Right, my only point here is that it’s better to throw an error when encountering bad or ambiguous input than trying to infer what should happen. I think tha a lot of problems in Js come from it being too accommodating regarding input, and the just trying to figure out what you might’ve meant. In vast majority of cases, an input of this kind if an indication of an error in the program logic and it’s better to fail on such inputs than to accept them. If somebody passes a date that doesn’t make sense for a current month, it’s almost certainly because they have some logic error in their code. Accepting this date as a parameter simply results in creating a subtle bug in a program that the user likely won’t be aware of and that’s going to be difficult to find in testing.

            • aebletrae [she/her]@hexbear.net
              link
              fedilink
              English
              arrow-up
              4
              ·
              8 months ago

              I agree with you that errors are useful feedback for coders who don’t know the ins and outs of an API. And every programmer is in that group at some point. But the difficulty in identifying this particular bug doesn’t stem from the API decisions.

              Whether Dates throw an error, or work with what they’re given, has no bearing on the subtlety of this bug. Either way, tests that don’t replace Date will fail to identify it most of the time, and tests that do, based on its use within the function, would be called wrong-headed by many.

              Either way, the bug only shows up at the end of months longer than the target month, and that infrequency has nothing to do with the peculiar design choices of the Date API. It stems exclusively from the evaluation of Date() called with no arguments returning different values at different times—behaviour you have not objected to, and which I’d expect to be considered entirely appropriate, in fact its very point—combined with an attempt to use that value, whatever it may be, without due consideration.

              Since the month is the only part of interest, there’s no reason to allow the other parts to vary at all. Fixing them, as I suggested at the beginning of all this, is the simplest approach, but setting them first, as has also been suggested, would work too.

              You can once again complain about JS design decisions and I’ll agree about many of them, but, as much as you might like it to be, and as annoying as so many of us think they often are, here it is beside the point. The perniciousness of this particular bug stems from unnecessarily calling a function with inconsistent output and then improperly processing that, instead of using a function call with always-predictable output.

              I’ve tried to point that out in all the ways I can think of, so if it’s still not getting through, I give up. And if your acknowledgement was too subtle for my sleepy brain, and I’ve ended up overexplaining, then I’m sorry.

              • ☆ Yσɠƚԋσʂ ☆OP
                link
                fedilink
                arrow-up
                4
                arrow-down
                8
                ·
                edit-2
                8 months ago

                The difference is that hrowing an error makes it much easier to find the bug early, while doing the wrong thing silently makes it much harder to do so. If an error is thrown by the API then the first time wrong input is supply the application will fail and you’ll know you have a problem. If the API silently does the wrong thing, then the application will keep doing the wrong thing until somebody notices and that tends to be far more costly.

                Finally, I’d like to note that this isn’t a hypothetical debate. This is how APIs work in sane languages such as Java:

                java.time.LocalDate.of(2023, 2, 31)
                > java.time.DateTimeException: Invalid date 'FEBRUARY 31'
                
                java.time.LocalDate.of(2023, 2, 3)
                > #object[java.time.LocalDate 0x2bc77260 "2023-02-03"]
                
                • aebletrae [she/her]@hexbear.net
                  link
                  fedilink
                  English
                  arrow-up
                  5
                  ·
                  8 months ago

                  Yes, and I’ve said that I agree with that in general. I know that this isn’t hypothetical; that’s exactly why I keep saying that throwing an error doesn’t help you find this bug early at all.

                  Even the silent weirdness can be caught by the most basic of tests checking output against input, but only if your function works the same way on every invocation.

                  Whether making a giant fuss (as you’d prefer) or making the best of it (as it actually does), the setMonth method always works the same way. My code always works the same way. The setDate suggestion makes the code always work the same way.

                  Code that always works the same way is easy to test.

                  If the day of the month is constant and incompatible with setMonth, whether there’s a thrown error or just an unwanted return value, a simple test will reveal that on every test run. If the day of the month is constant and always compatible with setMonth, the test will pass appropriately on every test run.

                  The bug in the code you originally presented comes from working differently over time. That’s why, most days, tests won’t identify the problem, even with a fussy, noisy API. Most testing days, the date will just happen to be compatible, and even the fussiest, noisiest API will carry on without any mention of the problem.

                  The reason the original code works differently over time has nothing to do with the silent, unexpected behaviour of setMonth. It’s entirely down to calling Date() without arguments, the entire point of which is to give different values over time. That call effectively introduces state that is not controlled by the function. And not bringing it under control is the real source of the bug.

                  Yes, absolutely, JavaScript sucks. Make F# the only supported web scripting language! But JavaScript’s suckiness is not the cause of this particular bug. JavaScript’s suckiness is not the reason this bug is hard to catch. The real problem lies in code that functions differently over time when it should (and could easily) be consistent. That’s what actually makes it hard to test.

                  Plenty of other languages and API design choices still allow code that functions that work differently over time, which is why, as justifiable as the complaints are in general, those factors are irrelevant for this particular bug. Write code that always works the same way and the problem goes away. That’s the real core of the issue.

                  Obviously, that’s easier said than done, and it’s irritating that neither loud errors nor most testing will help you in this regard, but that’s the way it is.

                  • ☆ Yσɠƚԋσʂ ☆OP
                    link
                    fedilink
                    arrow-up
                    4
                    arrow-down
                    9
                    ·
                    8 months ago

                    Whether making a giant fuss (as you’d prefer) or making the best of it (as it actually does), the setMonth method always works the same way. My code always works the same way. The setDate suggestion makes the code always work the same way.

                    You’re missing my point entirely here. Current behavior works in a SURPRISING way and SILENTLY produces an output that’s most likely to be unintended. Let me give you a concrete example of the problem here.

                    Let’s say you have a calendar app that shows the current day by default, and then there are buttons to go to next or previous month. To get the current day you’d have to call Date(), and then you’d have next and previous month functions that would work off the date you got. In Js world these functions will mostly work, but once in a while give users a wrong month silently.

                    The bug in the code you originally presented comes from working differently over time. That’s why, most days, tests won’t identify the problem, even with a fussy, noisy API. Most testing days, the date will just happen to be compatible, and even the fussiest, noisiest API will carry on without any mention of the problem.

                    That’s not the problem at all, and your whole line of argument here is frankly bizarre. There are plenty of use cases where you need to have functions that do something based on a current date. That means needing to get the date from the system without knowing what that date is up front by calling Date() without set arguments. This isn’t some anti pattern that you keep trying to make it out to be. There is absolutely nothing wrong with getting the current date.