Open zbraniecki opened 3 days ago
About LocaleMatcher specifically: this only affects data loading, right? I don't think most individual component constructors will have anything special to do with LocaleMatcher except for passing it down to the data provider. Things that are passed down to the data provider can be done externally via a custom data provider. This is just to say that I don't think LocaleMatcher by itself is a very strong argument in favor of requiring options.
What we've done in the past, and what I lean toward doing here, is to keep try_new
functions the way they are, and if we need a version with options in the future, we add try_new_with_options
, and in the next major release, we can switch it to be the default.
This is just to say that I don't think LocaleMatcher by itself is a very strong argument in favor of requiring options.
That's a great point. Basically we can handle ECMA-402 locale matcher even by something like:
let provider;
provider.setLocaleMatcherStrategy(LocaleMatcherStrategy::Foo);
let dtf = FooFormatter::try_new(provider, preferences);
this way we don't need it in Options.
What we've done in the past, and what I lean toward doing here, is to keep try_new functions the way they are, and if we need a version with options in the future, we add try_new_with_options, and in the next major release, we can switch it to be the default.
Yes, I'd like us to decide if we are ok having effectively three patterns for runtime arguments: 1) no arguments, 2) options bag, 3) single option. One half-way convergence would be to migrate (3) to (2) and have only two scenarios.
I think the following choices are fine:
try_new
with optionstry_new
with no options
try_new_with_options
and reconcile at next major releasetry_new_with_foo
with a positional argument named foo
try_new
Things we should not approve:
try_new
with a positional argument or any arguments other than provider, preferences and optionsThanks! Here's a list of cases that do not follow your heuristics:
./components/plurals/src/lib.rs
322: pub fn try_new_unstable(
368: pub fn try_new_cardinal_unstable(
425: pub fn try_new_ordinal_unstable(
618: pub fn try_new_unstable(
660: pub fn try_new_cardinal_unstable(
699: pub fn try_new_ordinal_unstable(
747: pub fn try_new_with_rules_unstable(
./components/datetime/src/neo.rs
186: pub fn try_new(
211: pub fn try_new_unstable<P>(
236: fn try_new_internal<P, L>(
411: pub fn try_new(
436: pub fn try_new_unstable<P>(
459: fn try_new_internal<P, L>(
./components/segmenter/src/sentence.rs
149: pub fn try_new_unstable<D>(provider: &D) -> Result<Self, DataError>
173: pub fn try_new_with_options_unstable<D>(
./components/segmenter/src/word.rs
238: pub fn try_new_auto_unstable<D>(provider: &D) -> Result<Self, DataError>
268: pub fn try_new_auto_with_options_unstable<D>(
362: pub fn try_new_lstm_unstable<D>(provider: &D) -> Result<Self, DataError>
391: pub fn try_new_lstm_with_options_unstable<D>(
476: pub fn try_new_dictionary_unstable<D>(provider: &D) -> Result<Self, DataError>
504: pub fn try_new_dictionary_with_options_unstable<D>(
./components/segmenter/src/line.rs
415: pub fn try_new_auto_unstable<D>(provider: &D) -> Result<Self, DataError>
456: pub fn try_new_lstm_unstable<D>(provider: &D) -> Result<Self, DataError>
494: pub fn try_new_dictionary_unstable<D>(provider: &D) -> Result<Self, DataError>
534: pub fn try_new_auto_with_options_unstable<D>(
584: pub fn try_new_lstm_with_options_unstable<D>(
641: pub fn try_new_dictionary_with_options_unstable<D>(
./components/calendar/src/any_calendar.rs
619: pub fn try_new_with_any_provider<P>(
666: pub fn try_new_with_buffer_provider<P>(
712: pub fn try_new_unstable<P>(provider: &P, kind: AnyCalendarKind) -> Result<Self, DataError>
782: pub fn try_new_for_locale_unstable<P>(
./components/calendar/src/week_of.rs
40: pub fn try_new_unstable<P>(provider: &P, locale: &DataLocale) -> Result<Self, DataError>
./components/experimental/src/transliterate/transliterator/mod.rs
242: pub fn try_new(locale: &Locale) -> Result<Self, DataError> {
251: pub fn try_new_with_any_provider(
263: pub fn try_new_with_buffer_provider(
275: pub fn try_new_unstable<PT, PN>(
329: pub fn try_new_with_override<F>(locale: &Locale, lookup: F) -> Result<Self, DataError>
342: pub fn try_new_with_override_with_any_provider<F>(
359: pub fn try_new_with_override_with_buffer_provider<F>(
376: pub fn try_new_with_override_unstable<PT, PN, F>(
./components/experimental/src/dimension/currency/long_formatter.rs
62: pub fn try_new(
103: pub fn try_new_unstable<D>(
./components/experimental/src/dimension/units/formatter.rs
95: pub fn try_new(
133: pub fn try_new_unstable<D>(
./components/experimental/src/displaynames/displaynames.rs
55: pub fn try_new_unstable<D: DataProvider<RegionDisplayNamesV1Marker> + ?Sized>(
122: pub fn try_new_unstable<D: DataProvider<ScriptDisplayNamesV1Marker> + ?Sized>(
190: pub fn try_new_unstable<D: DataProvider<VariantDisplayNamesV1Marker> + ?Sized>(
253: pub fn try_new_unstable<D: DataProvider<LanguageDisplayNamesV1Marker> + ?Sized>(
337: pub fn try_new_unstable<D>(
Thoughts:
In #5852 I
Left:
With introduction of Preferences we now have the final shape of the first argument to constructors and implemented the normalized model for how to converge Locale and Preferences.
The remaining question is how to handle the second argument which we currently have three approaches to:
OptionsBag
, similar toPreferencesBag
but for developer-induced optionsIt looks like this:
We have an opportunity to normalize it and conform all constructors to Option 1, if we want to, but it doesn't feel like a natural obvious choice, so I'd like to discuss it.
How we modeled preferences, locales and options
The current architecture separates
Preferences
which represent user-provided values fromOptions
which represent developer-provided values. This is novel and different from how ECMA-402 handles the constructors. In ECMA-402 we have(locale, options)
pair where Options is a bag combining keys set by user and developer and the user-ones get merged with locale extension keys.In our model the Preferences and Locale get merged as they represent the user-driven keys, and the Options are split into two "types".
First type are options that are affecting data slicing. In that case we push them to the constructor itself to allow for DCE to remove unnecessary code. Second type is options that don't affect DCE. It looks like this:
Value proposition of normalization
If we were to normalize the second argument I see three main value props:
localeMatcher
Why we shouldn't do this
There are several reasons for which we may not want to normalize it:
Default::default()
,None
oroptions!{}
to each constructor just to have consistency may not be worth it.LocaleMatcher
has questionable value, is basically never used and while we may have to find a way to support it, it is not clear if we should design ICU4X component API around it just becauseLocaleMatcher
exists.Counters:
Option<OptionsBag>
to allow forNone
if we think it's better DX thanDefault::default()
.LocaleMatcher
(to pass test-262) then we have to find a way to pass this argument to each constructor somehow... Even if no other Options are needed by that constructor.Is it urgent?
I'm not sure if it is. It would be nice to have a consistent, ECMA-402 compatible, constructor story for 2.0 and the PR is going to be quite simple to write if we decide to go for it, but it may be that we have different opinions and should give ourselves time to think it through and plan this for 3.0.