Live data from Hacker News

Good Design is Imperfect Design, Part 1: Honest Names

domainlanguage.com

81–90 of 133 posts

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

#81

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…

The problem is that "What is one month after 2021-02-28?" doesn't have a single meaning in plain human language . It could mean 30 days in the future. Or the same day of the week 4 weeks in the future (i.e., 28 days in the future). Or the day of the same cardinality in next month. Or any date in the next month. And those are all equally correct. It's simply not a precise measure of time when spoken from one human to…

Plus/Increment/Add 1 month, does have one single meaning, it's that the outcome may be invalid that is the issue.

"2021-01-31".plus(unit="Month", size=1) => "2021-02-31"

But nobody really wants that, because it's not a valid date. So implicitly the library is deciding to return a valid date.

A library could be written to just provide invalid dates, and let the end user handle any errors. That library could also include an explicit validation method that takes a date and returns a valid one.

"2021-02-31".coerceToValid() => "2021-02-28" // Overflow == Max

"2021-02-31".coerceToValid(asDays=True) => "2021-03-03" // Overflow Carries (to the right)

In fact the library, that provides an ignorant response and no contract on validity would hold to the associative property, it just wouldn't be as ergonomic.

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

#82
post #42

The problem is not the "plus" operator but instead lies in the "month" duration. Not all months last the same number of days, and while we are at it not all days have 24 hours! I'd rather move the explicit (and complicated) choice of what kind of month are you talking about (and thus also the relevant discussion about naming) into the operator that constructs a time interval.

I came across this problem once before and came to the same conclusion. “A month” is an ill-defined unit, so I just don’t allow it to be representable in datetime arithmetic. You make the point that “1 day” would also fall under this category, but it’s less variable and so it being equal to “24 hours” is good enough.

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

#83
post #57

Also ``` .plus(1 month) ``` The unknown behavior of 1-30 + 1 month is a red flag that maybe you shouldn't be using "month" as a unit of time because it isn't. Months suck. Bill for services every 10 days or every 25 days or every 50 days instead of every month.

> Bill for services every 10 days or every 25 days or every 50 days instead of every month.

Isn't that just prioritizing one's laziness as a developer over what makes sense to the user? My guess is most users would rather see the bill come out on the same day every month.

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

#84
post #40

Earlier quoted context omitted.

I did cover that: """ None of the examples in the article use a more vague syntax, such as "0 days before the end of the month". """ As another reply points out, it's incrementing the Month set of buckets. I'll also extend with other results I expect: 2020-12-31 .plus(1 months) => 2021-01-31 2020-12-31 .plus(2 months) => 2021-02-28 2020-12-31 .plus(3 months) => 2021-03-31 2020-12-31 .plus(4 months) => 2021-04-30 A no…

Maybe I'm just being thick (it's usually the case), but for the life of me I still don't know what your answer to the parent's question is, and I can't tell how your quoted part is supposed to answer it.

My 2 higher levels post post included an alternate phrasing of the test case they specified:

""" None of the examples in the article use a more vague syntax, such as "0 days before the end of the month". """

---

They asked:

""" What’s Feb 28th + 1 month?

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

---

It's implicit, the human only expects the month to change, because the input isn't a descriptive phrase "the end of the month" adjusted or not, it's a literal date. That's why my other test cases show the same behavior for the end of the month.

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

#85
post #75
post #48

Earlier quoted context omitted.

I think that's the easier case—most people would expect Feb 28th + 1 month = Mar 28th. The tricker one is Jan 31 + 1 month, because there is no Feb 31st. I don't think there is a "correct" answer to that.

If I pay you Jan 31, Feb 28, March 31, April 30, etc., don't I pay you monthly? Shouldn't I be able to express this as a repeated addition of a month? At any rate, these problems have been solved in finance, with proper date and schedule libraries.

No, you should be able to express it the way I mentioned in other examples: Base .plus( N months ) where N is whichever month after the reference you want.

.plus .plus .plus isn't correct because "x months" doesn't have a fixed size. You are NOT saying Base .plus(30 days), NOR are you saying Base .plus(4 weeks) ((which BTW, I'd expect to stay on the same weekday)). You're incrementing by an unstable value.

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

#86
post #62
post #47

Earlier quoted context omitted.

Inverting the date range probably confused the logic involved in accounting for leap dates. Possibly it fails to account for swapping the order of the dates to achieve an always positive result?

It's not (only?) related to leap years. It appears to be related to the month of April and some other weird stuff. -- leap year 2012: 2019-01-31 to 2012-01-30 --> 7 years 1 day 2012-01-30 to 2019-01-31 --> 7 years 1 day 2019-01-31 to 2012-02-29 --> 6 years 11 months 6 years 11 months 3 days 6 years 10 months 30 days (2012-02-30 does not exist) 2012-02-30 to 2019-01-31 --> 6 years 10 months 30 days (2012-02-30 does no…

Try your test cases in other languages. E.G. python3 import datetime ; (datetime.datetime.strptime("2019-01-31","%Y-%m-%d") - datetime.datetime.strptime("2012-02-29","%Y-%m-%d"))

The result, which reminds me of one of the other reasons I avoid python for trivial projects, is the duration / interval answer of days=2528.

Part of the bug is surely in Wolfram|Alpha returning the interval broken out in human durations; but importantly those durations __don't use fixed time values__. The precise length of a //year// and of a //month// are variable.

I still suspect there's double or single counting of leap-days in those durations as the reverse ones clash with converting a duration back to a whole number.

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

#87
post #57

Also ``` .plus(1 month) ``` The unknown behavior of 1-30 + 1 month is a red flag that maybe you shouldn't be using "month" as a unit of time because it isn't. Months suck. Bill for services every 10 days or every 25 days or every 50 days instead of every month.

Or bill on the X day of the month, where X <= 28.

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

#88
post #61

Earlier quoted context omitted.

But sometimes you need to bill at a certain point in the month. I don't pay my rent every X days, I pay it on the first of the month. The best solution for that may be something like establishing it as a frequency rather than accumulative addition. But that won't work in every situation. Dates are complex and require lots of thinking to do correctly in some circumstances.

> I don't pay my rent every X days, I pay it on the first of the month. I don't want to pay rent that way. It's a good way to get scammed because they're charging you a higher per-day rate in February than January. I'd much rather pay rent per day, if possible. If Amazon can take over my property manager I'm sure it'll be possible to bill it with the same flawless consistency of AWS billing, and have a concept of dis…

> I don't want to pay rent that way. It's a good way to get scammed because they're charging you a higher per-day rate in February than January.

Are you serious? They're not scamming you. They're giving you a discount 11 months out of the year. Feb is the only month They're charging you full rate for!

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

#89
post #61

Earlier quoted context omitted.

> I don't pay my rent every X days, I pay it on the first of the month. I don't want to pay rent that way. It's a good way to get scammed because they're charging you a higher per-day rate in February than January. I'd much rather pay rent per day, if possible. If Amazon can take over my property manager I'm sure it'll be possible to bill it with the same flawless consistency of AWS billing, and have a concept of dis…

In New Zealand we pay rent every 2 weeks instead of monthly and I’ve seen someone from the US come here and make the opposite argument. “They are scamming you because you have to pay an extra months rent every year”

It's a mixed bag in Aus.

Rent is listed per week prices but you'll see some landlords calculate it out by the day rate if you want to pay monthly. Others use the 52 week rate divided by 12.

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

#90
The problem is not in the `plus` method, as it's working as expected when the unit is in days.

It's the `month` unit that is misleading, since `1 month` looks like a constant value but really isn't, as the behaviour depends on the other operand, breaking associativity – or any expectation on how addition between two constants work really.

It would be less confusing if the API removed `month` as a period, and as a programmer you would have to write:

    periodLength = t.advanceMonths(1)
    t' = t.plus(periodLength days)
This way you can only `plus` in constant units (days), and `advanceMonths` is free to follow any heuristic and return anything depending on the value of `t` without breaking the expectations of `plus`, or you can have any number of functions with different definitions for "advance N months".
Post reply on HN