Live data from Hacker News

Good Design is Imperfect Design, Part 1: Honest Names

domainlanguage.com

11–20 of 133 posts

Re: Good Design is Imperfect Design, Part 1: Honest Names

#12
post #2

I disagree about "plus" in the JodaTime example. The month addition with corner cases did exactly what I expected because the whole library has been polished to "do what a normal human would do, most of the time". A normal human would not suspect Jan + 1 month, is any month other than Feb. However I suspect, not even reading the documentation, if it were fed 30 or 31 days, rather than '1 month', it would also do exac…

You expected 2021-03-31 + 1 month + 1 month to be different from 2021-03-31 + (1 month + 1 month)? I find this behavior understandable, but not apparent at first glance.

Also, FYI, this is not how GNU date works:

  $ date -d "Jan 28 next month" 
  Sun Feb 28 00:00:00 CST 2021
  $ date -d "Jan 29 next month" 
  Mon Mar 1 00:00:00 CST 2021
I could see confusion from this, depending on what libraries you are used to.

Re: Good Design is Imperfect Design, Part 1: Honest Names

#13
post #2

I disagree about "plus" in the JodaTime example. The month addition with corner cases did exactly what I expected because the whole library has been polished to "do what a normal human would do, most of the time". A normal human would not suspect Jan + 1 month, is any month other than Feb. However I suspect, not even reading the documentation, if it were fed 30 or 31 days, rather than '1 month', it would also do exac…

Disagree.

Jan + 1 month = February, sure.

But, as soon as you add the day, it falls apart for me.

For most dates, if I add a month, in my mental model, the answer is the next month with the same date.

Jan 15 + 1 month = Feb 15, etc

But, at the edges, it gets odd quickly.

Jan 31 + 1 month = ??? Not sure, maybe Feb 28, maybe Feb 29, maybe Mar 2, maybe Mar 3. Depends on the year and who's asking me to solve the problem.

I would expect any reasonable software to fail gracefully when asked to solve this problem. And by fail gracefully, I mean ask for clarification. Or prevent me from asking silly questions in the first place.

Re: Good Design is Imperfect Design, Part 1: Honest Names

#14
post #9

Please don’t have a method named plusIshRoundCeiling() in a library that “might” be used for years in lots of projects :) The plus() one is almost there and you can easily explain the ugly bits in javadocs (with lots of warnings around it so that it sticks out and is addressed hopefully in some next convenient cycle)

Can you explain why you see this name as unacceptable?

Re: Good Design is Imperfect Design, Part 1: Honest Names

#15
post #2

I disagree about "plus" in the JodaTime example. The month addition with corner cases did exactly what I expected because the whole library has been polished to "do what a normal human would do, most of the time". A normal human would not suspect Jan + 1 month, is any month other than Feb. However I suspect, not even reading the documentation, if it were fed 30 or 31 days, rather than '1 month', it would also do exac…

What is 2021-02-28 + 1 month? Is that 2021-03-28 or 2021-03-31? It is far from obvious what a normal human would do most of the time here. The lack of associativity is still a problem. If 2021-02-28 + 1 month = 2021-03-28,then (2021-02-28 + 1 month) + 1 month = 2021-03-28 + 1 month = 2021-04-28. While if I ask what is 2021-02-28 + 2 months (given 2021-02-28 + 1 month = 2021-03-31), most people would say 2021-04-30. W…

You seem to be stuck on: 'what is the value of 1 month' and thinking in terms of days, because that appears to be the precision of the left value.

Programmers exist in a world where things such as leap seconds matter. Normally if you have a timestamp that is just before a leap second, then add exactly a day's worth of seconds, you'd slide back a little in time. This might matter in another context, such as defining the limits of neighboring ranges properly. Also, who's to say the underlying precision is a second?

The intent of the library in question is to behave the way most people would. With imperfect buckets and idealized answers; yet also precision where someone makes the attempt to be specific.

None of the examples in the article use a more vague syntax, such as "0 days before the end of the month". They start with what a human might, a rounded but full date; then apply an interval. So a more clear contrived example might be.

  Jan 31 .plus(1 months) => Feb 28
  Jan 31 .plus(2 months) => Mar 31
  Jan 31 .plus(3 months) => Apr 30
  Jan 31 .plus(4 months) => May 31
  Jan 1 .plus(1 months) => Feb 1
  Jan 1 .plus(2 months) => Mar 1
  Jan 1 .plus(3 months) => Apr 1
  Jan 1 .plus(4 months) => May 1
Note how in the second half there are still variably sized months, but the result is what a human would want.

Re: Good Design is Imperfect Design, Part 1: Honest Names

#16
post #2

I disagree about "plus" in the JodaTime example. The month addition with corner cases did exactly what I expected because the whole library has been polished to "do what a normal human would do, most of the time". A normal human would not suspect Jan + 1 month, is any month other than Feb. However I suspect, not even reading the documentation, if it were fed 30 or 31 days, rather than '1 month', it would also do exac…

Disagree. Jan + 1 month = February, sure. But, as soon as you add the day, it falls apart for me. For most dates, if I add a month, in my mental model, the answer is the next month with the same date. Jan 15 + 1 month = Feb 15, etc But, at the edges, it gets odd quickly. Jan 31 + 1 month = ??? Not sure, maybe Feb 28, maybe Feb 29, maybe Mar 2, maybe Mar 3. Depends on the year and who's asking me to solve the problem.…

The point of the article is that it doesn't have to fail gracefully.

Consider the API:

    data Date = Date { getYear :: Int, getMonth :: Int, getDay :: Int }
        deriving (Eq, Ord, Show)

    addDays :: Date -> Int -> Date
    addMonthsRounded :: Date -> Int -> Date
Someone who does

    d `addMonthsRounded` 3
immediately has a contextual clue that there might be something fishy going on, and has a string they can google to get to the docs to find out that this "Rounded" business is all about "hey, the code

    let y = (x `addMonthsRounded` 1) `addMonthsRounded` (-1)
      in y == x
might sometimes return False because it truncates if your day doesn't fit in the given month."

Re: Good Design is Imperfect Design, Part 1: Honest Names

#17
post #12
post #2

I disagree about "plus" in the JodaTime example. The month addition with corner cases did exactly what I expected because the whole library has been polished to "do what a normal human would do, most of the time". A normal human would not suspect Jan + 1 month, is any month other than Feb. However I suspect, not even reading the documentation, if it were fed 30 or 31 days, rather than '1 month', it would also do exac…

You expected 2021-03-31 + 1 month + 1 month to be different from 2021-03-31 + (1 month + 1 month)? I find this behavior understandable, but not apparent at first glance. Also, FYI, this is not how GNU date works: $ date -d "Jan 28 next month" Sun Feb 28 00:00:00 CST 2021 $ date -d "Jan 29 next month" Mon Mar 1 00:00:00 CST 2021 I could see confusion from this, depending on what libraries you are used to.

The inconsistency with GNU date is more visible from Feb:

  $ date -d "jan 31 next month"
  Wed Mar  3 00:00:00 PST 2021
  $ date -d "jan 30 next month"
  Tue Mar  2 00:00:00 PST 2021
  $ date -d "jan 1 next month"
  Mon Feb  1 00:00:00 PST 2021
  $ date -d "feb 28 next month"
  Sun Mar 28 00:00:00 PDT 2021
Edit: This was on my unconscious mind for a bit and I came up with an additional test case to confirm a suspicion I realized.

  $ date -d "2016-1-31 next month"
  Wed Mar  2 00:00:00 PST 2016
  date -d "2016-2-1 next month"
  Tue Mar  1 00:00:00 PST 2016
  date -d "2016-2-1 next year"
  Wed Feb  1 00:00:00 PST 2017
  $ date -d "2016-3-1 next year"
  Wed Mar  1 00:00:00 PST 2017
GNU date will add the duration of the CURRENT interval (ignoring already occurred deviations, like leap years) relative to the specified base date.

The oddity in behavior I observed above is adding the length of the month of Jan to dates in Jan. I suspect only a programmer would find that inference remotely correct.

Re: Good Design is Imperfect Design, Part 1: Honest Names

#18
post #9

Please don’t have a method named plusIshRoundCeiling() in a library that “might” be used for years in lots of projects :) The plus() one is almost there and you can easily explain the ugly bits in javadocs (with lots of warnings around it so that it sticks out and is addressed hopefully in some next convenient cycle)

plusIshRoundCeiling() forces the reader to look at the javadocs to understand the edge cases, and in fact it serves as a reminder that there even are any edge cases.

Re: Good Design is Imperfect Design, Part 1: Honest Names

#19
post #15

Earlier quoted context omitted.

What is 2021-02-28 + 1 month? Is that 2021-03-28 or 2021-03-31? It is far from obvious what a normal human would do most of the time here. The lack of associativity is still a problem. If 2021-02-28 + 1 month = 2021-03-28,then (2021-02-28 + 1 month) + 1 month = 2021-03-28 + 1 month = 2021-04-28. While if I ask what is 2021-02-28 + 2 months (given 2021-02-28 + 1 month = 2021-03-31), most people would say 2021-04-30. W…

You seem to be stuck on: 'what is the value of 1 month' and thinking in terms of days, because that appears to be the precision of the left value. Programmers exist in a world where things such as leap seconds matter. Normally if you have a timestamp that is just before a leap second, then add exactly a day's worth of seconds, you'd slide back a little in time. This might matter in another context, such as defining t…

You’ve ignored your critique and answered only the more obvious anecdotes.

So I’ll repeat:

What’s Feb 28th + 1 month?

Does the human expect the last day of March? Or the 28th day of March?

The API doesn’t make that clear - I think you could reasonably argue for either.

Re: Good Design is Imperfect Design, Part 1: Honest Names

#20
That

    (some date + 1 month) + 1 month ≠ some date + 2 months
has always been true for floating numbers. This is exactly why you never trust fast matrix multiplications---they rely on cancelations of the form (a + b) - b = a + (b - b) = a.

I would argue that 1 month is just having too few significant digits. You can even implement it as returning 30 with probability 60% and returning 31 with probability 40%. Then on average you would have

    1 year ≈ sum([1 month] * 12)
Post reply on HN