I presume after validating the options, you’ll have to access them. Why not combine the two? Something like:
[parentheses, other_option] = Keyword.fetch_exact!(opts, [:parentheses, :other_option]) Perhaps even supporting defaults: [other_option, parentheses] = Keyword.fetch_exact!(opts, [:other_option, parentheses: false]) The name, of course, has to improve, but I think functionality-wise a function like this would be really useful. Michał. From: elixir-lang-core@googlegroups.com <elixir-lang-core@googlegroups.com> Date: Wednesday, 30 December 2020 at 10:53 To: elixir-lang-core@googlegroups.com <elixir-lang-core@googlegroups.com> Subject: Re: [elixir-core:9918] Validating keywords keys 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 <woj...@wojtekmach.pl<mailto:woj...@wojtekmach.pl>> 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 <jose.va...@dashbit.co<mailto:jose.va...@dashbit.co>> 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 <woj...@wojtekmach.pl<mailto:woj...@wojtekmach.pl>> 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 <devon.c.es...@gmail.com<mailto:devon.c.es...@gmail.com>> 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 <kurtis@rainbolt-greene.online<mailto:kurtis@rainbolt-greene.online>> 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 <an.leopa...@gmail.com<mailto:an.leopa...@gmail.com>> 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 <jose.va...@dashbit.co<mailto:jose.va...@dashbit.co>> 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 elixir-lang-core+unsubscr...@googlegroups.com<mailto:elixir-lang-core+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J8_RG5eeCZSw_c75Q4y19YFt-ipdnTAEa1cE2GnvwjrQ%40mail.gmail.com<https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J8_RG5eeCZSw_c75Q4y19YFt-ipdnTAEa1cE2GnvwjrQ%40mail.gmail.com?utm_medium=email&utm_source=footer>. -- 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 elixir-lang-core+unsubscr...@googlegroups.com<mailto:elixir-lang-core+unsubscr...@googlegroups.com>. 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<https://groups.google.com/d/msgid/elixir-lang-core/CAM9Rf%2BJPu8tF2VzNB4beDqO9jc%2BF-SDE6u%3D724EZm9271jY2ug%40mail.gmail.com?utm_medium=email&utm_source=footer>. -- 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 elixir-lang-core+unsubscr...@googlegroups.com<mailto:elixir-lang-core+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAMhJPGiKh3uOaY2UNDFYu9x64n-mM7Sqf7iHU09QeAmfOY0mwQ%40mail.gmail.com<https://groups.google.com/d/msgid/elixir-lang-core/CAMhJPGiKh3uOaY2UNDFYu9x64n-mM7Sqf7iHU09QeAmfOY0mwQ%40mail.gmail.com?utm_medium=email&utm_source=footer>. -- _________________ Devon Estes +49 176 2356 4717 www.devonestes.com<http://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 elixir-lang-core+unsubscr...@googlegroups.com<mailto:elixir-lang-core+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGowJcg_DWYAQsys5f6Ad1nYket8be1Lsrmui8Uh%3DzEAKzWzTQ%40mail.gmail.com<https://groups.google.com/d/msgid/elixir-lang-core/CAGowJcg_DWYAQsys5f6Ad1nYket8be1Lsrmui8Uh%3DzEAKzWzTQ%40mail.gmail.com?utm_medium=email&utm_source=footer>. -- 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 elixir-lang-core+unsubscr...@googlegroups.com<mailto:elixir-lang-core+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/5E7686D9-1DC6-4830-8C32-7FCAFFE6E706%40wojtekmach.pl<https://groups.google.com/d/msgid/elixir-lang-core/5E7686D9-1DC6-4830-8C32-7FCAFFE6E706%40wojtekmach.pl?utm_medium=email&utm_source=footer>. -- 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 elixir-lang-core+unsubscr...@googlegroups.com<mailto:elixir-lang-core+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B9YG1YwoK9JGAitzOzikOeo4dXCHyvu%3DjAU6SN1HRocw%40mail.gmail.com<https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2B9YG1YwoK9JGAitzOzikOeo4dXCHyvu%3DjAU6SN1HRocw%40mail.gmail.com?utm_medium=email&utm_source=footer>. -- 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 elixir-lang-core+unsubscr...@googlegroups.com<mailto:elixir-lang-core+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/2A76D025-BF9B-45E1-B268-DD23753FEC6C%40wojtekmach.pl<https://groups.google.com/d/msgid/elixir-lang-core/2A76D025-BF9B-45E1-B268-DD23753FEC6C%40wojtekmach.pl?utm_medium=email&utm_source=footer>. -- 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 elixir-lang-core+unsubscr...@googlegroups.com<mailto:elixir-lang-core+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4Jx0-0xU76qaury3k5P8WuKjNRj8xUKj1Cz8a0YyuX%2BMA%40mail.gmail.com<https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4Jx0-0xU76qaury3k5P8WuKjNRj8xUKj1Cz8a0YyuX%2BMA%40mail.gmail.com?utm_medium=email&utm_source=footer>. -- 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 elixir-lang-core+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/DB7PR07MB3899661710E2B7CE05CD82A0FAD70%40DB7PR07MB3899.eurprd07.prod.outlook.com.