Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

bestFit API choices #47

Closed
zbraniecki opened this issue Oct 17, 2017 · 10 comments
Closed

bestFit API choices #47

zbraniecki opened this issue Oct 17, 2017 · 10 comments
Labels

Comments

@zbraniecki
Copy link
Member

We decided to not include any bestFit algorithm in the first iteration of the RelativeTimeFormat API and instead we expect the client libraries like momentjs and others to add them on top.

I'm working currently on such higher-level wrapper and I encountered an interesting API design dilemma that I'd like to get a recommendation for.

When we designed the core formatters for the Intl API, we pushed all resolving pieces into the object constructors. That has two nice benefits - it does the computations and data retrieving at object creation, making the core operation (usually format) as lightweight as possible, and gives us resolvedOptions method that allows users to retrieve the values the object options were resolved to.

Now, in RelativeTimeFormat, we introduce a new concept - an option that is resolved at core operation calltime, rather than construction time. The format(value, unit) differs from all previous Intl APIs in the unit component.
Since it's a dummy operation which does not compute anything, that works, but both for higher level APIs and for the future bestFit, we should plan ahead how we're going to facilitate an API that has to select the unit when core operation is called, and how to provide an ability for the user to retrieve not only the result string, but also the unit it resolved to.

To present it as JS, I see two ways to do it:

let rtf = new Intl.RelativeTimeFormat('en-US', {style: 'short'});
let result = rtf.format(5, 'bestFit');
result === {'value': 'in 5 sec', unit: 'second'};

or:

let rtf = new Intl.RelativeTimeFormat('en-US', {style: 'short'});
let value = rtf.format(5, 'bestFit'); // 'in 5 sec'
let unit = rtf.resolveBestFitUnit(5); // 'second'

There of course may be others that I didn't think of :)

@rxaviers , @caridy , @littledan , @maggiepint, @icambron - can you share your preference?

@rxaviers
Copy link
Member

I encountered an interesting API design dilemma that I'd like to get a recommendation for.

Please could you clarify the API design dilemma? "An option that is resolved at core operation calltime, rather than construction time" opposed to "making the core operation (usually format) as lightweight as possible"?

Resolving the unit component at runtime should be sufficiently lightweight for runtime, right? [1] Did you find any trouble in particular please?

1: Resolving the unit component at runtime involves asserting the passed value, deducing entry (as a combination of unit and style) and internally picking an initialized-relativeTimeFormatter-at-construction-time from a hash/dictionary given entry, right? Noting that FormatNumber and ResolvePlural both have to happen anyway regardless of this unit resolution to happen at runtime.

@zbraniecki
Copy link
Member Author

Please could you clarify the API design dilemma?

The dilemma is how to provide user with both, a formatted value, and ability to learn what unit has been selected in the bestFit scenario, when the API only anticipates the resolution to happen at the constructor time and provides resolvedOptions to expose the resolved values.

The resolvedOptions do not apply when the resolution happens at format time.

We either have to return both, the formatted value and the unit, or provide two APIs - one for retrieving a formatted value and one for retrieving the unit bestFit resolves to.
I presented those two as examples.

There may be other solutions that I didn't think of.

@zbraniecki
Copy link
Member Author

The reason I'm bringing it up is because I believe that we should understand how we'll want to approach this in 2.0 API (when/if we add bestFit) to make the 1.0 forward-compatible.

@rxaviers
Copy link
Member

Thank you for the clarification @zbraniecki. I understand you're raising one additional challenge on top of the ones raised so far [1] for bestFit which basically is how the user should identify which unit was chosen by the bestFit resolution.

The reservation I have about your proposal (a) (the one that returns an object) is that the return type becomes different for the same function only because of a different option. What makes proposal (b) more appealing to me.

Alternatively, could we use parts to identify the unit? This is, could we use type returned by formatToParts to identify the unit? It seems like we're indeed using the unit when the output is numeric 1.1.3 5.f. We would have to "fix" it for the literal case...


1: In README.md, "... to implement a bestFit algorithm, which has its own API challenges with respect to standardizing an approach that works for all cases. See #7, #14, and #15. We'd probably need to provide a flag for users to fill, with no default setting, to choose between options for calendar calculation."

@icambron
Copy link

icambron commented Oct 17, 2017

I much prefer your second option, rtf.resolveBestFitUnit(5). Most users most of the time will just spit the result out and having to dereference the value just seems like an extra step. Also, if I understand correctly, your first option only returns an object if the unit argument is "bestFit" and a string otherwise. I generally find APIs where the value of the arguments control the return type to be frustrating. For example, if my code decides "bestFit" is the unit to use (as opposed to having it hardcoded), I also have to branch the code that interprets the result.

A possible alternative is for formatToParts to provide not just a value for the unit but also a normalized unit, like

[ ...
  { type: "unit", value: "sec", unit: "seconds"}
 ...
]

Where the unit is always the full string (second vs sec) and always in English. That requires a little more work on the user's part, though. Edit: I somehow missed that @rxaviers had this idea too.

As a separate matter, I'm not 100% sure I understand the example. Is it the case that when specifying "bestFit", the first argument is always considered to be in seconds? If so, I would have expected milliseconds there, so like:

rtf.format(5, 'bestFit') //=> "less than a second"
rtf.format(5000, 'bestFit') //=> "5 seconds"
rtf.format(5000000000, 'bestFit') //=> "2 years"

But perhaps this has already all been decided; I'm not trying to relitigate anything.

@caridy
Copy link
Collaborator

caridy commented Oct 25, 2017

Alternatively, could we use parts to identify the unit? This is, could we use type returned by formatToParts to identify the unit? It seems like we're indeed using the unit when the output is numeric 1.1.3 5.f. We would have to "fix" it for the literal case...

I agree with @rxaviers here. formatToParts is the mechanism that we have invented to offer you more details about the formatted value, and that one can have extension points to provide any sort of detail that you want to offer.

This is also a very edge case, just like the other cases. Imagine that you want to know if the weekday is part of the formatted value when using DateTimeFormat. The fact that it is part of the resolvedOptions doesn't really mean that it will be part of the final output, so they only way you have to know that is by inspecting the parts.

Aside from that, option1 is not really an option because all formatters are suppose to produce a string value rather than object. And option2 is probably ok, but I will argue that formatToParts should be sufficient.

@rxaviers
Copy link
Member

Cool. The action item to move on with formatToParts is to

  • "fix" it for the literal case...

@caridy
Copy link
Collaborator

caridy commented Oct 27, 2017

Well, if you show "yesterday" that doesn't have to be a literal, it could be something else that specifies the unit notation of the text as well. @icambron suggestion seems to be fine, maybe bikeshedding on the value of type.

@zbraniecki
Copy link
Member Author

I like the ftp approach. Wondering if there's a way for us to improve the datetime and number ftp to give more data.

@littledan
Copy link
Member

This question seems to be answered, so I'm closing this issue. A built-in "bestFit" API could be added in a follow-on proposal, as Intl.RelativeTimeFormat is already at Stage 4.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants