I really like the idea of a convenience function in the Keyword module. One idea that comes to mind:
Keyword.limit_keys!([parentheses: 10], [:parenthesis]) raises or returns 1st param unchanged. -Greg > On Dec 30, 2020, at 4:53 AM, José Valim <[email protected]> wrote: > > The issue is that for take!, I can see two semantics: > > 1. The map/keyword must have all of the given keys > 2. The map/keyword must have at most the given keys > > And I think 1) makes more sense intuitively. :( > > On Wed, Dec 30, 2020 at 11:48 AM Wojtek Mach <[email protected]> wrote: > Fair enough, agreed about decoupling the problem. In that case I’d still offer > Keyword.take!/2 that works like this: > > iex> Keyword.take!([a: 1], [:a, :b]) > [a: 1] > > iex> Keyword.take!([c: 1], [:a, :b]) > ** (ArgumentError) > > I think take/2 and take!/2 matches struct/2 and struct!/2. > >> On 30 Dec 2020, at 11:30, José Valim <[email protected]> wrote: >> >> Wojtek, I originally thought about Map.merge!, where the second argument >> must be a subset of the first. This way we can check keys and provide >> default values: >> >> Keyword.merge!([parenthesis: 10], opts) >> >> However, when I tried using this in practice, I realized that default >> arguments are not always straight-forward to compute. For example, you may >> want to compute them lazily. You could argue we could set them to nil in >> said cases, but then we'd mix the absence of a key with nil value, which may >> not be desired. >> >> Therefore, I concluded that it is probably best to keep those problems >> separated and validate only the keys. I agree with Andrea that this is small >> but the benefit I see having it in core is to promote more folks to use it. >> Both Python and Ruby provide at the syntax-level a convenience that checks >> only the given keys are expected. So, when it comes to options, both of >> these languages are allowing us to write assertive code more elegantly than >> Elixir. >> >> >> On Wed, Dec 30, 2020 at 10:10 AM Wojtek Mach <[email protected]> wrote: >> I think this would be a great addition to the core. >> >> While there are libraries in this space, as silly as this may seem, solving >> this key typo problem seems like solving the 60%-80% case (not to take away >> anything from those libraries!) >> >> How about a Keyword.take!/2? >> >> iex> Keyword.take!([a: 1], [:a, :b]) >> [a: 1] >> >> iex> Keyword.take!([c: 1], [:a, :b]) >> ** (ArgumentError) unknown key :c in [c: 1] >> >> There are however two problems with it: >> >> 1. would people expect that `Keyword.take!([a: 1], [:a, :b])` should fail >> because `:b` is not in the input? >> >> Maybe the 2nd argument accepts defaults? (I know it probably starts doing >> too much...) >> >> iex> Keyword.take!([a: 1], [:a, :b]) >> [a: 1, b: nil] >> >> iex> Keyword.take!([a: 1], [:a, b: 2]) >> [a: 1, b: 2] >> >> In fact this could have the following semantics: if there's no default, >> it's >> a required key: >> >> iex> Keyword.take!([], [:a, b: 2]) >> ** (ArgumentError) missing required key :a >> >> What's nice is you can later use `Keyword.fetch!/2` that will save you >> from >> typos. >> >> But that being said, If the 2nd argument accepts a keyword, then it >> probably shouldn't be called `take!/2` as it no longer matches `take/2`. >> >> 2. If you do: `opts = Keyword.take!(..., ...)` and later `opts[:my_key]` you >> still have an opportunity for a typo and you can't necessarily use >> `Keyword.fetch!/2` because optional keys might not be there. >> >> As Devon mentioned, structs are a really cool solution because they provide >> rigidity, defaults, and the assertive map access syntax with ".". Creating a >> struct for every function that accepts options feels like a bit much though. >> >> Taking everything above into consideration, perhaps there's: >> >> iex> Map.something_something!([], [:name, timeout: 5000]) >> ** (ArgumentError) missing required key :name >> >> iex> opts = Map.something_something!([name: Foo], [:name, timeout: 5000]) >> iex> opts.timeout >> 5000 >> >> and I feel like it's still relatively small addition but it's closer to the >> "80% solution". No idea how to name this thing though! >> >> >> >>> On 30 Dec 2020, at 09:36, Devon Estes <[email protected]> wrote: >>> >>> Typos are extremely hard to prevent in dynamic data structures since >>> validations need to be implemented at the point of use instead of at the >>> point of creation/definition of the structure. What would stop the >>> developer from writing the typo in their validation, as José did in his >>> example? >>> >>> It seems to me like if the goal is to prevent typos then a struct would be >>> the way to go. >>> >>> Kurtis Rainbolt-Greene <[email protected]> schrieb am Mi. 30. >>> Dez. 2020 um 09:29: >>> Yes, but think of the valuable hours saved and the amount of code that >>> won't have to be written. >>> >>> I mean even Valim's own example again has the typo. >>> >>> On Tue, Dec 29, 2020 at 11:58 PM Andrea Leopardi <[email protected]> >>> wrote: >>> Considering how straightforward the code you showed is, and that for more >>> complex scenarios we have libraries like nimble_options, I might be >>> slightly hesitant to add this to core. >>> >>> Andrea >>> >>> On Wed, 30 Dec 2020 at 08:53, José Valim <[email protected]> wrote: >>> Hi everyone, >>> >>> I am working on a new project and yesterday I spent a couple hours on a bug >>> due to a in a keyword list. In a nutshell, I was supposed to pass >>> parenthesis: 10 as keywords to a function but I passed parentheses: 10. >>> >>> I have fixed the issue by adding the following code: >>> >>> for {k, _} <- keyword, k not in [:parentheses, :other_options], do: >>> raise "unknown key #{inspect(k)} in #{inspect(keyword)}" >>> >>> The code is super straight-forward but I am wondering if we should add it >>> to Elixir to promote said validation. What do you think? Any suggestions on >>> where it should be defined and with which name? >>> >>> Thank you! >>> >>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "elixir-lang-core" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to [email protected]. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J8_RG5eeCZSw_c75Q4y19YFt-ipdnTAEa1cE2GnvwjrQ%40mail.gmail.com. >>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "elixir-lang-core" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to [email protected]. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/elixir-lang-core/CAM9Rf%2BJPu8tF2VzNB4beDqO9jc%2BF-SDE6u%3D724EZm9271jY2ug%40mail.gmail.com. >>> >>> >>> -- >>> Kurtis Rainbolt-Greene, >>> Software Developer & Founder of Difference Engineers >>> 202-643-2263 >>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "elixir-lang-core" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to [email protected]. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/elixir-lang-core/CAMhJPGiKh3uOaY2UNDFYu9x64n-mM7Sqf7iHU09QeAmfOY0mwQ%40mail.gmail.com. >>> -- >>> >>> _________________ >>> Devon Estes >>> +49 176 2356 4717 >>> www.devonestes.com >>> >>> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "elixir-lang-core" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to [email protected]. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/elixir-lang-core/CAGowJcg_DWYAQsys5f6Ad1nYket8be1Lsrmui8Uh%3DzEAKzWzTQ%40mail.gmail.com. >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected]. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/5E7686D9-1DC6-4830-8C32-7FCAFFE6E706%40wojtekmach.pl. >> >> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected]. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B9YG1YwoK9JGAitzOzikOeo4dXCHyvu%3DjAU6SN1HRocw%40mail.gmail.com. > > > -- > You received this message because you are subscribed to the Google Groups > "elixir-lang-core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-core/2A76D025-BF9B-45E1-B268-DD23753FEC6C%40wojtekmach.pl. > > -- > You received this message because you are subscribed to the Google Groups > "elixir-lang-core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4Jx0-0xU76qaury3k5P8WuKjNRj8xUKj1Cz8a0YyuX%2BMA%40mail.gmail.com. -- You received this message because you are subscribed to the Google Groups "elixir-lang-core" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/F639C82D-9AB5-4CBC-991C-EC895C8037E3%40gmail.com.
