Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
As an Elixir novice, I think one of the hardest things about getting started inElixir is figuring out what kind of data structures to use. My teamhas recently started going all in on Elixir, so Iâve been trying to brush upin earnest. Often, however, Iâll be reading over some of my teamâs code and have a hard time deciphering what Iâm even looking at. The syntax looks very similar to Ruby (which I know pretty well), but the patterns, conventions, and data structures are just slightly different. In my mind, that makes sense since itâs a functional rather than object-oriented language: where you would use objects in Ruby, youâre probably spawning processes instead in Elixir.
But anyway, since Iâm learning Elixir now, I thought itâd be useful toprovide a cheat sheet or overview of the differences in data structures Iâve noticed as a Rubyist exploring Elixir.
Data Types
If youâre coming from Ruby (or most other programming languages), integers,floating-point numbers, ranges, and regular expressions are all probablyfamiliar to you. Fortunately, those all exist in Elixir too. There are a fewdifferences, but I havenât dealt too much with them yet.
Atoms are like symbols in Ruby. They begin with colons and their names aretheir values. For example, :hellois a valid atom in Elixir. Theyâre oftenused to tag values.
There are also strings in Elixir. Strings always have double quotation marks, while charlists are in single quotations marks. Strings are binaries, and charlists are actually just lists of code points. I have rarely used charlists so far.
Hereâs a quick glance at what those types look like
iex> 2 # integeriex> 2.0 # floating pointiex> false # booleaniex> 1..4 # rangeiex> ~r/hello/ # regular expressioniex> :hello # atomiex> âworldâ # stringiex> âworldâ # charlist
Elixir has the additional data types, Port and PID, whichare used in process communication. They are entities that are made availablethrough the Erlang VM.
Port
A Portis used to communicate (read/write) to resources outside yourapplication. They are great for starting operating system processes andcommunicating with them. For example, you might want to open a port to run an OS command, like echo.
You could open a port and send it a message, like this:
iex> port = Port.open({:spawn, âecho supâ}, [:binary])#Port<0.1305>
Then, you can use the flush() IEx helper to print the messages from theport.
iex> port = Port.open({:spawn, âecho supâ}, [:binary])#Port<0.1305>iex> flush()iex> {#Port<0.1305>, {:data, âsup\nâ}}iex> :ok
You can send a port any name of a binary you want to execute. For example,from the directory of my jekyll blog, I opened up an iex session, opened aport, and then sent the bundle install command, which installed all theRuby gem dependencies. Hereâs a snippet of the output.
iex> port = Port.open({:spawn, âbundle installâ}, [:binary])#Port<0.1306>iex> flush(){#Port<0.1306>, {:data, âUsing concurrent-ruby 1.0.5\nâ}}{#Port<0.1306>, {:data, âUsing i18n 0.9.5\nâ}}{#Port<0.1306>, {:data, âUsing minitest 5.11.3\nâ}}{#Port<0.1306>, {:data, âUsing thread_safe 0.3.6\nâ}}{#Port<0.1306>, {:data, âUsing tzinfo 1.2.5\nâ}}{#Port<0.1306>, {:data, âUsing activesupport 4.2.10\nâ}}{#Port<0.1306>, {:data, âUsing public_suffix 2.0.5\nâ}}{#Port<0.1306>, {:data, âUsing addressable 2.5.2\nâ}}{#Port<0.1306>, {:data, âUsing bundler 1.16.2\nâ}}{#Port<0.1306>, {:data, âUsing coffee-script-source 1.11.1\nâ}}{#Port<0.1306>, {:data, âUsing execjs 2.7.0\nâ}}{#Port<0.1306>, {:data, âBundle complete! 4 Gemfile dependencies, 85 gems now installed.\nUse `bundle info [gemname]` to see where a bundled gem is installed.\nâ}}:ok
PID
A PID is a reference to a process. Whenever you spawn a new process, youâllget a new PID. Expect to talk a lot about PIDs. Youâll probably need to holdonto PIDs so you can send different processes messages.
Hereâs an example of spawning a process and getting the PIDÂ back.
iex> pid = spawn fn -> IO.puts(âhello worldâ) endiex> hello worldiex> #PID<0.123.0>
The process dies after it has done its job. PIDs and Ports warrant their own standalone post, but for now, I think itâs sufficient to just be aware that they exist.
So, now that weâve added our new types, this is our basic cheat sheet.
The Elixir Data Type Cheat Sheet
iex> 2 # integeriex> 2.0 # floating pointiex> false # booleaniex> 1..4 # rangeiex> ~r/hello/ # regular expressioniex> :hello # atomiex> âworldâ # stringiex> âworldâ # charlistiex> #Port<0.1306> # portiex> #PID<0.123.0> # pid
The real challenge with Elixir in my opinion, though, is figuring out how toorganize these basic data types into structures you can use. So letâs take alook at the various collection types and why you would use each.
Collection Types
Here are the collection types youâll likely encounter:- Tuples- Lists- Keyword Lists- Maps-Â Structs
Youâve probably heard those words before, at least in passing, but if youâre accustomed to Ruby, youâre probably wondering why you need all those extra types of collections. Letâs investigate.
Tuples
Tuples are ordered collections of values. They look like this:
iex> {:hello, âworldâ}iex> {1, 2}iex> {:ok, âthis is amazing!â, 2}
# You can check if itâs really a tupleiex> tuple = { âhelloâ, âworldâ}iex> is_tuple tupleiex> true
# and then you can get an element from a tuple by indexiex> elem(tuple, 1)iex> âworldâ
I think tuples are a little wild. I mean, they look like they should be hashes, but they sort of behave like Ruby arrays. And then theyâre called tuples! Itâll pay off to get familiar with them though, which is what I keep telling myself when I get confused for the hundredth time.
Tuples crop up all over the place in Elixir. Return values of functions are often tuples that you can pattern match on, so it makes sense to start seeing the world through tuples. Tuples usually have two to four elements, and at this point, theyâre my go-to data structure. When youâre dealing with data structures that have more than four elements, thatâs probably a good case for using a map or struct instead.
Lists
Lists are linked data structures. They look like this:
iex> [1, 2, 3, 4]iex> [âhelloâ, âworldâ]
In Ruby, youâd think that was an array, but in Elixir, itâs a list! Because lists are implemented as linked data structures, theyâre good for recursion, but bad for randomly retrieving an element or even figuring out the length because youâd need to traverse the whole list to figure out the size. To date, Iâve mostly been using tuples instead of lists. If you had to choose between them, I suppose youâd need to consider the expected size of the collection and what kind of operations youâll be performing on it.
Keyword Lists
To further complicate matters, there are also such things as keyword lists in Elixir. In essence, this is a list of two-value tuples.
# keyword listiex> [ phrase: âoh helloâ, name: âtracyâ ]
# is actually two-value tuplesiex> [ {:phrase, âoh helloâ}, {:name, âtracyâ} ]
This continues to baffle me, even though I am aware of its general existence. The cool thing about keyword lists is that you can have two of the same keys in a keyword list.
iex> keyword_list = [food: âpeanut butterâ, food: âice creamâ, flavor: âchocolateâ] # a valid keyword list
Keyword lists are good for command-line parameters and options.
Maps
Next up are maps. If you wanted a real key-value store, not a list of key-values, this is what youâre looking for. They look a bit like hashes in Ruby.
iex> %{âgreetingâ => âhelloâ, ânounâ => âworldâ}iex> %{:greeting => âhelloâ, :noun => âworldâ}iex> %{greeting: âhelloâ, noun: âworldâ} # if the keys are atoms, you can skip the hash rockets.
iex> greeting = %{spanish: âholaâ, chinese: âni haoâ, english: âhelloâ}iex> greeting[:spanish]iex> âholaâiex> greeting.chineseiex> âni haoâ
Maps are good for passing associative data around, and pretty much everything else that is bigger than tuple size.
Structs
Structs are like enhanced maps. They permit only certain keys and those keys must be atoms. Structs need to be defined in modules with reasonable default values. Theyâre maps with rules.
iex> defmodule IceCream doâŠ. defstruct flavor: ââ, quantity: 0âŠ. end
iex> chocolate = %IceCream{flavor: âchocolateâ}iex> chocolate.flavoriex> âchocolateâ
Youâll see that the struct is defined with the same % percent symbol as a map, but itâs followed by the name of the module. Thatâs how I remind myself that theyâre just stricter maps.
Older versions of Elixir used to also include the HashDict to handle maps with more than a couple hundred values, but that module has been deprecated in favor of the good olâfashioned Map.
That brings us to the end of the common data types and collection types youâll see in Elixir. Although there are a number of differences between the two languages, there are some similarities. Of course, thereâs plenty more to learn about Elixir, the conventions, and the cool things you can do, but this (I think) is a good start to getting familiar with the language. Hopefully thisâll serve as a decent guide to deciphering any Elixir you might encounter soon!
The Elixir Collection Cheat Sheet
iex> {:ok, âthis is amazing!â, 2} # tupleiex> [âhelloâ, âworldâ] # listiex> [ phrase: âoh helloâ, name: âtracyâ ] # keyword listiex> greeting = %{spanish: âholaâ, chinese: âni haoâ, english: âhelloâ} # mapiex> chocolate = %IceCream{flavor: âchocolateâ} # struct
Resources
- Basic Types on Elixir Lang- Programming Elixir by Dave Thomas- Hex Docs
Which Data Structure Should I Use? An Elixir Cheat Sheet was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.