
On Mon, Jul 11, 2005 at 02:53:25AM -0700, Ashley Yakeley wrote:
A normalising construction function is a good idea. But if we hide the GregorianDay constructor, two of your use cases become slightly harder:
"How do I truncate a date to the first of the month?"
"How do I truncate a date to the first day of the year it occurred in?"
I dislike the "let the user muck with the data structure and expect the library to figure out what he meant" style. For one thing, it means that at some point, there is an intermediate value whose semantics are unclear. (Can you document what will happen if it is passed to all functions?) For another, the reader of the code has to look ahead to see what kind of normalization is eventually done. There are enough ambiguous cases in date handling that I would prefer to be explicit about everything. As a date API user, I think that those use cases become more clear in the style that Brian suggested. d2 = d1 `changeDayOfMonth` 1 d2 = d1 `changeDayOfYear` 1 Now, I've only followed this thread loosely (these examples may be inconsistent with the proposed API, sorry), and I realize that this style has disadvantages: it may be tedious or slow if you have to combine many operations. But I fear the alternative is a mess of difficult-to-document-and-remember normalization rules for invalid dates.
This is actually quite appealing, though it's a rather radical change. The answers to the use-cases above become
d' = makeGregorianTruncate (gregorianYear d) (gregorianMonth d) 1
d' = makeGregorianTruncate (gregorianYear d) 1 1
Opinions?
Nobody's going to like that. :-)
Is it better to have simple functions for each combination (your scheme) or selector functions (my original scheme)?
I tend to like the former, because the ambiguous cases for the different units are different, and can be documented separately. Easier for the programmer to understand what he's asking for. Andrew