Skip to content
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

How does the client hook a data provider to a formatter API? #150

Closed
sffc opened this issue Jun 25, 2020 · 6 comments
Closed

How does the client hook a data provider to a formatter API? #150

sffc opened this issue Jun 25, 2020 · 6 comments
Assignees
Labels
A-design Area: Architecture or design C-data-infra Component: provider, datagen, fallback, adapters R-obsolete Resolution: This issue is no longer relevant T-docs-tests Type: Code change outside core library
Milestone

Comments

@sffc
Copy link
Member

sffc commented Jun 25, 2020

Should the data provider be a global setting, or a required parameter to the formatter, or a combination of both?

@sffc sffc added A-design Area: Architecture or design C-data-infra Component: provider, datagen, fallback, adapters discuss Discuss at a future ICU4X-SC meeting T-core Type: Required functionality labels Jun 25, 2020
@sffc sffc self-assigned this Jun 25, 2020
@sffc
Copy link
Member Author

sffc commented Jul 10, 2020

We could use a factory model.

struct NumberFormatFactory<'a> {
    pub data_provider: &'a dyn DataProvider,
}

impl NumberFormatFactory {
    pub fn create(&self, locale: &Locale, options: &NumberFormatOptions) {
        NumberFormat::try_new(locale, self.data_provider, options);
    }
}

An advantage is that each type could have its own data provider trait that could be backed by something other than the stock ICU data provider. For example, PluralRulesFactory could be constructed from either a stock data provider or a custom data provider that provides pre-compiled functions.

@nciric nciric mentioned this issue Jul 16, 2020
@nciric
Copy link
Contributor

nciric commented Jul 16, 2020

If I follow your example (pardon my syntax):

# Covers locales from [aa - de)
static_provider = NumberFormatFactory.data_provider = StaticProvider(static_code);

# Covers locales from [de - ..)
dynamic_provider = NumberFormatFactory.data_provider = DynamicProvider(url_to_fetch_data_from);

How would end developer know which one to use, given the users locale? Do we need a layer over this that would do language matching and picking the correct factory?

@echeran
Copy link
Contributor

echeran commented Jul 16, 2020

Is this question about the ergonomic API (seeing as that's what the user would be using directly more often)? IIRC, did we already discuss and agree that the low-level/logical API would always have the data provider be an argument?

Even in the ergonomic API, I think we should always allow the data provider to be an argument, to always expose flexibility/simplicity to the user. I would be against the option of only exposing them via a global setting -- I think this creates complexity that causes inflexibility. I've experienced this type of complexity with Okapi, which comes with pre-made file format handlers and allows you to create and use your own handlers, but it stores all handlers in handler registries (which can be global and/or local), and becomes more complicated when you have multiple implementations of handlers for a single file format, and then those implementations have multiple versions of themselves.

@sffc
Copy link
Member Author

sffc commented Jul 16, 2020

If I follow your example (pardon my syntax):

# Covers locales from [aa - de)
static_provider = NumberFormatFactory.data_provider = StaticProvider(static_code);

# Covers locales from [de - ..)
dynamic_provider = NumberFormatFactory.data_provider = DynamicProvider(url_to_fetch_data_from);

How would end developer know which one to use, given the users locale? Do we need a layer over this that would do language matching and picking the correct factory?

The idea is that you would implement another data provider that wraps the two.

struct MixedProvider(StaticProvider, DynamicProvider);
impl DataProvider for MixedProvider {
  fn load(&self, req: Request) -> Result<Response, Error> {
    if (req.locale < "de") {  // (not a real comparison)
      return self.0.load(req);
    } else {
      return self.1.load(req);
    }
  }
}

Then, the MixedProvider becomes the one that you give to the ICU4X APIs.

Is this question about the ergonomic API (seeing as that's what the user would be using directly more often)?

Yes; this is about the ergonomic layer, and it relates to every ergonomic API implementation, not only Rust but the ones in every other language we choose to target.

IIRC, did we already discuss and agree that the low-level/logical API would always have the data provider be an argument?

Yes, sort-of. The data should be pulled out of the data provider before passing it as an argument to the logical API.

@sffc
Copy link
Member Author

sffc commented Jul 16, 2020

Conclusion from 2020-07-16: Make the data provider an argument in the constructor for now. Later, look if there are ways to add factories or macros to help.

@sffc sffc added T-docs-tests Type: Code change outside core library and removed discuss Discuss at a future ICU4X-SC meeting T-core Type: Required functionality labels Jul 16, 2020
@sffc sffc added this to the 2020 Q3 milestone Jul 16, 2020
@sffc
Copy link
Member Author

sffc commented Aug 19, 2020

CC @markusicu @macchiati

@sffc sffc modified the milestones: 2020 Q3, ICU4X 0.2 Oct 20, 2020
@sffc sffc added the R-obsolete Resolution: This issue is no longer relevant label Mar 12, 2021
@sffc sffc closed this as completed Mar 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-design Area: Architecture or design C-data-infra Component: provider, datagen, fallback, adapters R-obsolete Resolution: This issue is no longer relevant T-docs-tests Type: Code change outside core library
Projects
None yet
Development

No branches or pull requests

3 participants