Created
July 22, 2021 15:23
-
-
Save anandgeorge/ba6f9ed03c9a6fba532dbea68d612f6e to your computer and use it in GitHub Desktop.
Elixir Data Types, Maps, Lists, Tuples and Pattern Matching TIps
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Access help functions for a module using the h command; so | |
h String | |
will give you help related to the String function | |
#### Data types | |
iex> 2 # integer | |
iex> 2.0 # floating point | |
iex> false # boolean | |
iex> 1..4 # range | |
iex> ~r/hello/ # regular expression | |
iex> :hello # atom | |
iex> "world" # string | |
iex> 'world' # charlist | |
iex> #Port<0.1306> # port | |
iex> #PID<0.123.0> # pid | |
You should always enclose strings within double quotes. | |
If you use single quotes, that creates a charlist, which is a different type. | |
Variable names cannot begin with a captial letter | |
iex(4)> Radius = 2 | |
** (MatchError) no match of right hand side value: 2 | |
#### Modules | |
Module names use CamelCase starting with a capital letter. Function names use snake_case. | |
defmodule Calculator.Area is a perfectly valid module name without defining Calculator separately | |
Modules can be nested and referred using the dot notation | |
defmodule Calculator do | |
defmodule Area do | |
def square(a) do | |
a * a | |
end | |
end | |
end | |
The function can be referred to by calling Calculator.Area.square(5) | |
#### Import modules | |
Modules can be imported and the functions therein can be called directly | |
import Calculator.Area then we can call | |
square(5) directly | |
However importing Calculator does not allow you to call | |
Area.square(5) | |
Limiting the import | |
defmodule Rectangle do | |
def area(a) do | |
a * a | |
end | |
def area(a, b) do | |
a * b | |
end | |
end | |
import Rectangle, only: [area: 2] only gives access to specific functions | |
area(4) will throw an error | |
area(4, 5) works | |
Alias on the other hand sets the alias of the module. So functions have to be still called in the context of the submodule. | |
They cannot be called directly as in the case of import | |
alias Calculator.Area, as: Area or | |
alias Calculator.Area, as: A | |
Atoms start with a : | |
Atoms are usually written in snake_case | |
Strings | |
first_name = "Anand" | |
"Anand" | |
last_name = "George" | |
"George" | |
name = first_name <> " " <> last_name # String concatenation | |
"Anand George" | |
greeting = "Hello #{first_name}!" # Replacing variables in a string | |
"Hello Anand!" | |
Pipe operator | |
String.reverse("house") \ # the backslash is used to tell the interpreter to continue on the next line | |
|> String.capitalize() | |
#### Lists | |
Lists store multiple values, and they can contain different types. A list is enclosed in brackets [] | |
A list is a linked list so it stores values in sequence. | |
[1, 2, 3, 4] | |
[5 ,4, true, false] | |
Adding and subtracting lists uses the ++ and -- operator | |
[1, 2] ++ [2, 4] | |
[1, "a", 2, false, true] -- ["a", 2] | |
hd returns the first element of the list and tl returns the rest of the list | |
shopping_list = ["apple", "orange", "banana", "pineapple"] | |
hd(shopping_list) | |
"apple" | |
tl(shopping_list) | |
["orange", "banana", "pineapple"] | |
hd([]) and tl([]) returns an error | |
length returns length of the list | |
length([1, 2]) returns 2 | |
numbers = [1,5,3,7,2,3,9,5,3] | |
Enum.max(numbers) | |
Enum.sort(numbers) | |
Enum.join(["nothing", "like", "the", "sun"], " ") | |
#### Tuples | |
Tuples are arrays that can hold multiple elements. Tuples can be accessed by index. | |
They are meant to hold a fixed number of related elements. | |
{1, 2, 3} | |
{:ok, "test"} | |
result = {:ok, "Lorem ipsum"} | |
elem(result, 1) | |
"Lorem ipsum" | |
b = Tuple.append(results, "Test") | |
c = Tuple.delete_at(b, 1) | |
d = Tuple.insert_at(b, 1, "ipsum") | |
new_list = Tuple.to_list(d) | |
tuple_size(d) | |
#### Keyword lists | |
Keyword lists are key-value data structures, in which keys are atoms and keys can appear more than once. | |
list1 = [ name: "Miguel Palhas", email: "[email protected]" ] | |
As it turns out, this is just syntactic sugar for a list, where each value is a 2-element tuple. It is equivalent to: | |
list2 = [ | |
{ :name, "Miguel Palhas"}, | |
{ :email, "[email protected]" } | |
] | |
user = [{:name, "joe"}, {:age, 23}] | |
or | |
anuser = [name: "joe", age: 23] | |
user[:name] | |
Keyword.get(user, :age) | |
23 | |
Keyword.delete([length: 78, width: 104], :length) | |
[width: 104] | |
#### Maps | |
Maps are a real key-value store. A key has to be unique within a map. | |
Maps are good for passing associative data around, and pretty much everything else that is bigger than tuple size. | |
iex> %{"greeting" => "hello", "noun" => "world"} | |
Using atoms as keys | |
product_prices = %{apple: 0.5, orange: 0.7} | |
%{apple: 0.5, orange: 0.7} | |
product_prices.apple | |
0.5 | |
product_prices = %{apple: 0.5, orange: 0.7, coconut: 1} | |
%{apple: 0.5, coconut: 1, orange: 0.7} | |
Map.to_list(product_prices) | |
[apple: 0.5, coconut: 1, orange: 0.7] | |
Map.values(product_prices) | |
[0.5, 1, 0.7] | |
Map.split(product_prices, [:orange, :apple]) | |
{%{apple: 0.5, orange: 0.7}, %{coconut: 1}} | |
Map.delete(product_prices, :orange) | |
%{apple: 0.5, coconut: 1} | |
additional_prices = %{banana: 0.4, pineapple: 1.2} | |
%{banana: 0.4, pineapple: 1.2} | |
Map.merge(product_prices, additional_prices) | |
%{apple: 0.5, banana: 0.4, coconut: 1, orange: 0.7, pineapple: 1.2} | |
c = Map.put(product_prices, :potato, 0.2) | |
%{apple: 0.5, coconut: 1, orange: 0.7, potato: 0.2} | |
#### Structs | |
Structs are like enhanced maps. They permit only certain keys and those keys must be atoms. | |
They need to be defined in modules with reasonable default values. They're maps with rules. | |
A struct guarantees that only the defined fields are allowed. | |
iex> defmodule IceCream do | |
.... defstruct flavor: "", quantity: 0 | |
.... end | |
iex> chocolate = %IceCream{flavor: "chocolate"} | |
iex> chocolate.flavor | |
iex> "chocolate" | |
#### Tuples vs Maps | |
In the Erlang VM, a tuple is always stored as a contiguous segment of memory – | |
the item at index n is stored at the memory address right after where n-1 ended. | |
As such, it's generally very bad form to do a lot of modifying of tuple content and structure, | |
as a good number of those operations will result in data being copied and collated at a new memory address. | |
Contrast this to lists, which the Erlang VM keeps track of by storing a reference to the previous | |
data point's address alongside the current data point. As such, not only is the length of a list dynamic, | |
its addresses within RAM are, too (and the newest data is, paradoxically, usually at the leftmost side of the list). | |
Matching a Map works a little bit different to matching a Tuple or List. You can match just against the values you are interested in. | |
#### Pattern Matching | |
{b, c} = {10, 15} | |
{10, 15} | |
b | |
10 | |
#### Pattern matching Lists | |
shopping_list = ["apple", "orange", "banana", "pineapple"] | |
["apple", "orange", "banana", "pineapple"] | |
[head | tail] = shopping_list | |
["apple", "orange", "banana", "pineapple"] | |
head | |
"apple" | |
tail | |
["orange", "banana", "pineapple"] | |
[a | b] = shopping_list | |
["apple", "orange", "banana", "pineapple"] | |
a | |
"apple" | |
b | |
["orange", "banana", "pineapple"] | |
[a, b, c, d] = shopping_list | |
["apple", "orange", "banana", "pineapple"] | |
a | |
"apple" | |
c | |
"banana" | |
[a | _] = shopping_list # Wildcard pattern matching | |
["apple", "orange", "banana", "pineapple"] | |
a | |
"apple" | |
[first_product, second_product | tail] = shopping_list | |
["apple", "orange", "banana", "pineapple"] | |
second_product | |
"orange" | |
#### Pattern Matching Maps | |
product_prices = %{apple: 0.5, orange: 0.7, pineapple: 1} | |
%{apple: 0.5, orange: 0.7, pineapple: 1} | |
%{orange: price} = product_prices | |
%{apple: 0.5, orange: 0.7, pineapple: 1} | |
price | |
#### Pattern Matching Strings | |
user = "Anand George" | |
"Anand George" | |
"Anand " <> last_name = user | |
"Anand George" | |
last_name | |
"George" | |
#### Pattern Matching arguments | |
defmodule Area do | |
def circle(:exact, radius) do | |
3.14159265359 * radius * radius | |
end | |
def circle(:normal, radius) do | |
3.14 * radius * radius | |
end | |
def circle(radius) do | |
circle(:normal, radius) | |
end | |
end | |
#### Functions with guard clauses | |
defmodule Law do | |
def can_vote?(age) when is_integer(age) and age > 17 do | |
true | |
end | |
def can_vote?(age) when is_integer(age) do | |
false | |
end | |
def can_vote?(_age) do | |
raise ArgumentError, "age should be an integer" | |
end | |
end | |
#### Case with multiple conditions and patter matching | |
case result do | |
{:ok, file} -> "The file exists" | |
{:error, reason} -> "There was an error" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment