Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keyword.merge behaviour when a list is the second argument (docs/test) #5395

Closed
PragTob opened this issue Nov 2, 2016 · 10 comments
Closed

Comments

@PragTob
Copy link
Contributor

PragTob commented Nov 2, 2016

Environment

  • Elixir version (elixir -v):
Erlang/OTP 19 [erts-8.0] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

Elixir 1.3.4
  • Operating system:
tobi@happy ~ $ uname -a
Linux happy 3.19.0-32-generic #37~14.04.1-Ubuntu SMP Thu Oct 22 09:41:40 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

Current behavior

The current behaviour is that when Keyword.merge is called with a normal list as the second argument it just appends the list.

iex(1)> Keyword.merge [a: 1], [b: 2]
[a: 1, b: 2]
iex(2)> Keyword.merge [a: 1], [2, 3]
[{:a, 1}, 2, 3]

Expected behavior

Not sure, part of what this is about :) I see 3 options:

  1. throw an error (as the second argument is no keyword list).
  2. The current behavior can be argued for and could be declared wanted behaviour, in that case I'd want to add a unit test for it and some documentation (there doesn't seem to be any for this case atm)
  3. it could be declared "undefined behaviour" and marked as such

I'd have expected 1.) to happen but through the internal representation can also understand that 2.) might be a good course of action.

Thanks for all your effort + Cheers,
Tobi

@eksperimental
Copy link
Contributor

eksperimental commented Nov 3, 2016

Great catch @PragTob
I think we should throw an error.

looking at the source code this function is not optimized, as we traverse keyword2, as many times as keyword1 elements we have.
I can work on it unless @PragTob is planning on taking a stab.

@PragTob
Copy link
Contributor Author

PragTob commented Nov 3, 2016

I'd love to take a stab at this :) Not sure I'm proficient enough with Elixir yet to fix the performance but I might as well try and learn :D

Which reminds me, Map.merge/3 is also significantly slower than Map.merge/2 - might be related.

@josevalim
Copy link
Member

I am not sure yet how we should solve this issue but it is worth mentioning that most functions in keyword will accept "improper" keywords:

Keyword.get [:a, :b, c: 3], :c
3

@whatyouhide
Copy link
Member

@PragTob Map.merge/2 is faster than Map.merge/3 because it calls to maps:merge/2 which is implemented in C, while Map.merge/3 manually builds the resulting map with maps:fold/3.

@whatyouhide
Copy link
Member

What do we need to discuss in order to move this forward? :)

@PragTob
Copy link
Contributor Author

PragTob commented Nov 19, 2016

As I understood it's me getting up my lazy... bottom to try and implement it that it only really works with kwlists but I haven't gotten to it for #reasons. So to not block this further, please anyone go ahead and implement it - I might still if I get to it.

Thanks for triaging issues @whatyouhide ! :)

@eksperimental
Copy link
Contributor

@josevalim specs mention that both args should be keywords ([{atom, any}]). Will it be considered a backward incompatible change if we throw an error when one is not provided?

@josevalim
Copy link
Member

Looking at the history, we changed the Keyword module in the past to raise for functions that expected a Keyword but a mixed list was given.

On the other hand, we had to do changes like this one in the past to support mixed formats when interfacing with Erlang.

Balancing the facts, I believe we should remain "pure", i.e. only keywords list allowed, because the cases we interface with Erlang are rather rare.

So we should fix this, it is mostly a matter of finding the most efficient implementation.

@josevalim
Copy link
Member

@eksperimental I don't believe it is backwards incompatible to change merge. I am unsure if we should change get though because of efficiency. Furthermore, we should be super diligent with building (i.e. we should not build invalid keywords), but it is a lesser problem when reading. Similar to how Strings rely on auto synchronization when reading UTF-8.

@eksperimental
Copy link
Contributor

so should we fix any other function that takes improper lists or non-keyword lists?

eksperimental added a commit to eksperimental-forks/elixir that referenced this issue Nov 20, 2016
eksperimental added a commit to eksperimental-forks/elixir that referenced this issue Nov 25, 2016
Similar to
elixir-lang#5466

It makes sure a valid keyword is given as both arguments.

Additionally, it adds tests to make sure Keyword.merge/2 and /3
work exactly the same way.

Related issue:
elixir-lang#5395
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

4 participants