Hi Jens, On Fri, Jan 22, 2021 at 1:18 PM Dirk Eddelbuettel <e...@debian.org> wrote:
> > On 22 January 2021 at 21:35, Jens Heumann wrote: > | Dear r-devel, > | > | Today I came across what I would call inconsistencies in the `c.Date` > | method compared to what happens when concatenating other classes: 1. > | Non-commutativity: The type in the arrangements of the elements does > | matter (first element is critical), 2. the resulting value is numeric > | instead of expected integer (as in the case with factors). > | > | > ## > | > Examples#################################################################### > | > ## 1. Non-commutativity: > | > c(.1, Sys.Date()) > | [1] 0.1 18649.0 > | > c(as.integer(.1), Sys.Date()) > | [1] 0 18649 > | > ## whereas: > | > c(Sys.Date(), .1) > | Error in as.Date.numeric(e) : 'origin' must be supplied > | > c(Sys.Date(), as.integer(.1)) > | Error in as.Date.numeric(e) : 'origin' must be supplied > | > > | > ## 2. Numeric instead of numeric value > | > str(c(as.integer(.1), Sys.Date())) > | num [1:2] 0 18649 ## not integer > | > > | > ################################################################################ > > | > | > | I'm not sure if `c.Date` should redefined, since there would probably be > | many more classes to consider. However, the error message "'origin' must > | be supplied" cannot be served by the user and appears to me like an > | imperfection in the design. > | > | It would be desirable if `c.Date` harmonizes with the hierarchy stated > | in `?c`: "NULL < raw < logical < integer < double < complex < character > | < list < expression. [...] factors are treated only via their internal > | integer codes" and behaves best like a factor (and also throws integer > | values as in 2. above). > So I think the issue here is twofold. The first (fairly subtle) issue here is that "Date" is not a type, it's an "(S3) class" (which, in turn, is just a labeling attribute, as illustrated by the fact that it's removed by c.default). > typeof(Sys.Date()) [1] "double" So Date cannot appear anywhere in that hierarchy, because it is a hierarchy of types. The reason c(as.integer(1), Sys.Date()) gives a numeric, is in fact because of that type hierarchy, as one of the elements is a "double" (the Date), which precludes returning an integer. The second issue is is that S3 dispatch occurs only on the first element, so you're hitting completely different methods with c(as.integer(1), Sys.Date()) and c(Sys.Date(), as.integer(1)) So that is where your non-commutativity is coming from. Personally, I think the case for c(<Date>, stuff) to give you a Date object back is stronger than that of commutativity, given the caveat that it would only be commutative if c(<Date>, <character w/ date string>) gave you a character back and c(<Date>, <numeric>) gave you a numeric back, but I can see there's space to disagree about that and argue for commutative absolutism. Note though that there is unlikely to be a way to get c(<character date string>, <Date>) to give you a Date back, because, again, S3 dispatch only sees the first argument, so you would *at best* be in c.character (but in as it stands now, in c.default, which explicitly strips classes). I do agree though that imho, the error from the latter could at least be improved. Unfortunately, Date objects do not carry around their origin, so we cannot do something such as "use the same origin as the Date object which caused dispatch into this method for any non-Date objects", which was going to be my suggestion here until I checked its feasibility. Best, ~G | > | Or maybe disabling non-dates at all `if (!all(sapply(list(Sys.Date(), > | .1), "class") == "Date")) stop...`, but this is a little beyond my > | knowledge. > | > | Anyway, I hope my remark is of relevance and contributes to the > | continuous development of our great programming language R! > > Nice analysis, well done. Sadly it is also a "known feature" of the c() > operator and documented as such -- S3 class attributes drop. C'est la vie. > From ?c > > ‘c’ is sometimes used for its side effect of removing attributes > except names, for example to turn an array into a vector. > ‘as.vector’ is a more intuitive way to do this, but also drops > names. Note that methods other than the default are not required > to do this (and they will almost certainly preserve a class > attribute). > > I have into that trap approximately 4.56e8 times in this idiom > > > for (d in Sys.Date() + 0:2) print(d) > [1] 18649 > [1] 18650 > [1] 18651 > > > > Eventually one learns to switch to an iterator, and to pick the dates from > a > vector preserving their class. > > Dirk > > -- > https://dirk.eddelbuettel.com | @eddelbuettel | e...@debian.org > > ______________________________________________ > R-devel@r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel > [[alternative HTML version deleted]] ______________________________________________ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel