Map.merge with structs

1,838 views
Skip to first unread message

vadim

unread,
Mar 24, 2016, 11:18:44 PM3/24/16
to elixir-lang-core
I noticed peculiar behavior of `Map.merge` when applied to structs.

For example I used range:

```elixir
iex(7)> a = 1..3
1..3

iex(8)> a.__struct__
Range

iex(9)> Map.delete(a, :__struct__)
%{first: 1, last: 3}

iex(13)> x = Map.merge(a, %{first: 2, middle: 5})
2..3

iex(14)> Map.delete(x, :__struct__)
%{first: 2, last: 3, middle: 5}
```

This behavior is quite unexpected, given that attempt to set `middle` explicitly would fail.

```elixir
iex(15)> %{a | middle: 12}                       
** (KeyError) key :middle not found in: 1..3
    (stdlib) :maps.update(:middle, 12, 1..3)
    (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1262: :lists.foldl/3
```

There was a discussion back in 2014 titled [Dict.merge_existing/2](https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!searchin/elixir-lang-core/Dict.merge_existing$2F2/elixir-lang-core/OYtzRKNzLWs/zyHr8vVZ2KgJ), which does not appear to result in code. 

Can someone explain difficulties with the proposed solution, or why the breach of struct's contract is lesser evil? 

Thank you,

/vadim

vadim

unread,
Mar 25, 2016, 12:14:40 AM3/25/16
to elixir-lang-core

For reading convenience (sorry for missing formatting):

I noticed peculiar behavior of Map.merge when applied to structs.

For example I used range:

iex(7)> a = 1..3

1..3

iex(8)> a.__struct__
Range

iex(9)> Map.delete(a, :__struct__)
%{first: 1, last: 3}

iex(13)> x = Map.merge(a, %{first: 2, middle: 5})
2..3

iex(14)> Map.delete(x, :__struct__)
%{first: 2, last: 3, middle: 5}

This behavior is quite unexpected, given that attempt to set middle explicitly would fail.

iex(15)> %{a | middle: 12}                       
** (KeyError) key :middle not found in: 1..3

    (stdlib) :maps.update(:middle, 12, 1..3)
    (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1262: :lists.foldl/3

There was a discussion back in 2014 titled Dict.merge_existing/2, which does not appear to result in code.

José Valim

unread,
Mar 25, 2016, 2:48:44 AM3/25/16
to elixir-l...@googlegroups.com
The Map module will always bypass any struct "safety" mechanism. If you want to merge a map into a struct, use Kernel.struct/2:

struct(1..3, last: 5, middle: 3)



José Valim
Skype: jv.ptec
Founder and Director of R&D

--
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-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/bbea9d99-b912-4182-b4c5-6e9e7ccc775b%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

vadim

unread,
Mar 25, 2016, 4:45:53 PM3/25/16
to elixir-lang-core, jose....@plataformatec.com.br
Thank you!
Reply all
Reply to author
Forward
0 new messages