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.

Reply via email to