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.

Reply via email to