GrammAcc

GrammAcc

Disclaimer: I’m still learning, so not an Elixir expert.

This second option seems the best to me. Even if the type system can make sense of a big bag of atoms, having things namespaced like this makes things easier to reason about for everyone as long as it’s not nested imo.

Another option would be to allow the user to register their custom options with the lib via a macro, so that all options can be treated the same and verified at compile time by checking them against the list of registered atoms.

2
Post #11
fuelen

fuelen OP

Core doesn’t actually raise on unknown keys here. Both inspect/2 and String.split/3 just ignore them:

iex> inspect(1, my_option: 1)
"1"

iex> String.split("Hello World", " ", test: 1)
["Hello", "World"]

custom_options option was only added in 1.9.0. The struct itself existed long before that. A struct is not an open map by definition, so when the need for caller-supplied extras showed up, the only place to put them was a separate custom_options field. So I wouldn’t read it as evidence that the closed/struct approach is the idiomatic one. There wasn’t really a choice given the existing shape.

Logger is in the standard library too, and it goes the other way from Inspect.Opts. It uses a single flat keyword for everything. Name collisions are possible, but they’re handled by convention and documentation, not by validation or a separate field.

Logger.info("hello", ansi_color: :green, whatever: 1)

That’s more about reading, rather than typing.
I wouldn’t say map is correct in this case and keyword isn’t. Theoretically, there might be options in the future that’ll benefit from duplicate keys :thinking:

You forgot about wrapping type to a list :slight_smile: [EDIT: I misread shared options as open options and came to this example] With shared options it makes sense to use parameterized open_keyword type:

@type open_keyword(t) :: [t | {atom(), term()}]

@type string_option :: {:trim, boolean()} | ...
@type shared_option :: {:nilable, boolean()} | ...

@type string_type :: :string | {:string, open_keyword(string_option() | shared_option())}

Actually… it looks nice :smiley:

Yeah, I know Req library uses this approach. But Mold positions itself as a lightweight, shape-first library where schemas are plain data and ergonomics matter more than catching mistyped option names – String.split/3 doesn’t check options and that’s not the end of the world.

2
Post #12
mudasobwa

mudasobwa

Creator of Cure

Yeah, I am the author of the PR allowing custom options. AFAIR, my initial intent was to treat anything unknown as custom, but there was a rock-solid argument we should distinguish custom stuff and the expected one.

Also, inspect/2 is a Kernel helper, I was talking about the protocol implementation.

1
Post #13
— All posts loaded —