-
Notifications
You must be signed in to change notification settings - Fork 162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Easier way to combine multiple Temporal objects in one from
or with
call ?
#720
Comments
I'm not sure the drawbacks of the chained methods are so severe as to balance adding more complexity, so my first preference would be to leave it as-is. But assuming for the moment that a change is needed, as a counter-proposal, how about accepting multiple arguments, much like in Object.assign()? That would solve the concerns about adding new property names and conflicting between fields (since the order will be well-defined). const dateTime = Temporal.DateTime.from(yearMonth, { day: 1 }, time);
const localDateTime = Temporal.LocalDateTime.from(yearMonth, { day: 1 }, time, { timeZone: 'Europe/Paris' }); |
Won't that break the |
It would require some care, but it would be possible to have the last argument be either an options bag or a fields bag. |
Seems reasonable. I'd suggest an array instead of multiple args, because with a multi-arg solution IDE autocomplete would suggest options property names AND instance property names for the second and later parameters. This would degrade IDE usability for the Regardless of array vs. multi-arg, IMHO the property name syntax still feels more discoverable and easier to use, esp. in an IDE where autocomplete would suggest all allowed property names. (It'd also show an overload for an array, but overloads are shown in popup help windows that are easier to ignore, while property-name autocomplete is right in the middle of the user's code so is much more discoverable.) Also, if we did #724 (which is essentially the inverse of this suggestion) then it'd probably tip the scales in favor of property names. |
I've been running into this as well. Despite #403 (comment) , I don't see a clear rationale in the meeting notes for why fields must be exposed as accessors on prototypes rather than enumerable own data properties on instances (which would align with Records and trivially support naïve object spread). At the risk of arguable relitigation, I'd like to document an explicit decision on this point. However, I don't like the redundancy and potential for inconsistency opened up by checking fields bags for composite values. |
const date = Temporal.Date.from({yearMonth, monthDay}); I don't like this, because both The status quo is fine. Either chain your methods, or spread from EDIT: Since Time doesn't carry a calendar (at least not yet), this isn't as big of a problem as I thought. I still think the status quo is fine though. |
If they carry different calendars, it should throw - not sure what else would make sense? |
I prefer the status quo. I could be convinced that an array of objects |
I don't see any way to defeat the issues noted above (conflicts, ordering, etc.), so my current opinion on my own suggestion is 👎. @ptomato's array suggestion could be implemented later without breaking anything, so I'd support it if we get a lot of user demand for it. That said, if we decide to eliminate conversion methods in favor of
I agree. Requiring |
@gilmoreorless's proposal in #592 and the latest discussion on #747 prompted me to resurrect this issue with two significant changes that I just updated in the OP: a) always throw if there's a possibility of a conflict. If this proposal is just an ergonomic shortcut for spreading the result of b) per #592, we should accept strings like |
This and #889 both leave me feeling like something is not quite right, but I'm not sure I can articulate it. Maybe the closest is Richard's remark from today's meeting about how there is no field called |
Are we thinking too much like physicists? We know that under the covers each of these types has slots that store each DateTime field separately. So we naturally assume that the only way to get data in or out of these types should be via those same fields. But what if we didn't know the internal data model, or if the data model of a DateTime was a Even if it is a little weird, the ergonomics & consistency benefits seem pretty large, especially now that strings can substitute for objects. Given that brevity is our top feedback, it may be worth some compromise here.
BTW, here's a few mental models I've been toying with. Let me know if any of these resonate. Possible Model 1: Expansion Aliases I've been thinking of Possible Model 2: Flattening Nested Objects This one simply says that Temporal functions always flatten their inputs. If you pass the equivalent of |
I think of them as expansions. If you have |
I think you picked an easy case. What about Then there are the ripple effects—accepting a I don't deny the ergonomic improvements, but I do question whether they outweigh the increased complexity. From my perspective, it would be a better tradeoff to instead special-case support for |
Yep. This is why I proposed that we should throw if the units could possibly overlap. It's too hard to predict what the default behavior should be. If users want to handle overlaps, they can use the more complex syntax by spreading
A problem is that
This is especially risky with LDT because order dependency may only show up around DST boundaries which most developers won't think to test before putting code in production. For this reason, I believe that we shouldn't offer a variant of Another way to say it is that we should try to make "best practice" code also the easiest code to write... or at least not much harder than riskier patterns. |
That's true, but I don't see how it relates to the point;
Aren't mixed date/time immutations already deviating from best practices? I think "different time on the same day" is plausible (and even that is probably 80% start-of-day and 19% end-of-day), "same time on a different day" is possible, and anything else to be so rare that requiring the extra typing of |
Meeting, Sept. 18: We'll drop this proposal, due to the concern of |
EDIT 2020-09-11: Updated this proposal to align with #747 and #592. The main changes are: a) always throwing if there's an overlap; b) allowing strings and property bags.
While adapting cookbook samples to
LocalDateTime
, I found it to be awkward to combine multiple Temporal objects usingwith()
andfrom()
. The same challenges exist with existing types likeDate
andDateTime
so I'm filing a separate issue to discuss. Examples:After using these patterns in many cookbook samples and tests, I found the "chained methods" solution to be intuitive for doing a single concatenation but problematic when doing several at once:
dateTime.with(yearMonth).with({day: 30})
may not return the same result asdateTime.with({day: 30}).with(yearMonth)
for some dates, e.g.2020-02-29
.The "spread properties" solution is IMHO clearer about what's going on ("assemble a new DateTime from various parts") and is immune to order dependence. I also like it because there's no new methods to learn: you just spread stuff into the object parameter. But its syntax is quite awkward. Its performance characteristics probably aren't great either.
Is there a compromise solution possible that allows adding Temporal objects to a
from
/with
property bag without spreading? @ptomato's feedback on #700 gave me an idea: could we accept aggregate "fields" liketime
ordate
in the property bags passed tofrom
andwith
methods ofDate
,DateTime
, andLocalDateTime
? Like this:Here's a specific proposal:
Date
'sfrom
andwith
would acceptyearMonth
andmonthDay
properties.DateTime
'sfrom
andwith
would acceptdate
,time
,yearMonth
, andmonthDay
properties.LocalDateTime
'sfrom
andwith
would acceptdateTime
,date
,time
,yearMonth
, andmonthDay
properties.These fields would not be emitted by
getFields
. They're only for making input methods more ergonomic.If any input fields overlap (e.g.
time
andhour
, ordayMonth
andday
) then throw a RangeError. In the polyfill, the error message should note the conflict, e.g.'time' property conflicts with 'hour'
.undefined
. If two inputs overlap and neither's value isundefined
, then throw an exception even if the corresponding property values are identical. This behavior is designed to catch errors at development time instead of varying behavior at runtime. If users want overlapping, they can do the non-ergonomic approach of spreading every object..from()
whose result is spread into the input property bag. This implies that any valid argument to a type'sfrom
method should also be accepted as a value of the property for that type. Here are examples using theTime
type:from
,with
, and conversion methods liketoDateTime
.Thoughts?
The text was updated successfully, but these errors were encountered: