[054] Maps, Part 1

An introduction to maps, a new feature in Erlang R17 and Elixir 0.13

Subscribe now

Maps, Part 1 [05.13.2016]

Today we're going to talk about Maps. Maps are a data structure that was introduced in Erlang R17. At the time of this episode, it's still a release candidate, but I want to go ahead and introduce them since they're an important data structure. They're coming up in Elixir 0.13, which isn't yet merged into master.

I've linked early discussion of Maps in Elixir in the Resources section of the episode script. The actual implementation differs a bit from that, so I've also linked to a preliminary guide on them for the elixir-lang site. Obviously once 0.13 is released, they'll show up in official documentation like you'd expect.

Preparation

In order to follow along, you'll need to have installed the elixir v0.13 branch, which requires Erlang R17. I've been using kerl to manage my Erlang installations. I want to look at using kiex to manage my Elixir installations, but I haven't taken the time yet.

To install Erlang r17 with kerl, you would just do the following:

kerl build 17.0-rc1 r17rc1
kerl install r17rc1 ~/erlang/erlangs/r17rc1

Then you can activate it with:

. ~/erlang/erlangs/r17rc1/activate

Then, you'll want to check out the proper branch of elixir:

cd ~/elixir/elixir
git checkout v0.13

Finally, make clean, make, and make install

make clean
make
make install

I've already done all of these, but I wanted to make sure I went over the process.

Basics

Maps in Elixir are essentially the same thing as Hashes in Ruby or Maps in Scala. They're built on top of the new Map data type added in Erlang R17. They provide implementations for the Access, Enum, and Inspect protocols. They are intended to replace public Records that we've used in the past in Elixir. Let's have a look at how to use them.

Launch an iex session, and let's have a look.

To make a Map, you can just write it out:

iex(1)> %{:foo => "bar"}
%{foo: "bar"}

As the inspected value of that expression implies, you can also use the HashDict style to make a map, if your keys are atoms:

iex(2)> map = %{foo: "bar", baz: "whee"}
%{baz: "whee", foo: "bar"}

The syntax for updating them is a bit funny at first:

iex(11)> %{map | foo: "BAR"}
%{baz: "whee", foo: "BAR"}

You'll get an error if you try to update a key that doesn't exist:

iex(15)> %{map | crazy: :key}
** (ArgumentError) argument error
    (stdlib) :maps.update(:crazy, :key, %{baz: "whee", foo: "bar"})

You can access their values like you would a hash in Ruby:

iex(12)> map[:foo]
"bar"

If the key is an atom, you can also access the value another way:

iex(14)> map.foo
"bar"

So here you can see where they fit in like records in at least one substantial way.

You can match on maps, as you'd expect:

iex(3)> %{foo: something} = map
%{baz: "whee", foo: "bar"}
iex(4)> something
"bar"

A huge benefit this has over keyword lists is that the ordering doesn't matter in matches. That was one of the primary usability deficiencies that keyword lists had over maps in practice.

Since a match is successful for any subset, the empty Map matches any map:

iex(13)> %{} = map
%{baz: "whee", foo: "bar"}

If you try to match with a key that isn't in the map on the right hand side, you'll get a MatchError:

iex(14)> %{crazy: key} = map
** (MatchError) no match of right hand side value: %{baz: "whee", foo: "bar"}

Summary

That's it for our quick introduction to Maps. In the next episode, we'll look at Maps a bit further. They're a big new feature to the both Elixir and Erlang, and they're worth spending a little time on. I wanted to introduce them so we could use them in BEAM Toolbox later. See you soon!

Resources