Learn Elixir - Part 2
Conditionals
Operators and if sentences
1 == 1
Please don’t write this ugly code
embrace immutability, adopt trasnforming structs
use pipe operator |>
step_1 = (1 + 2 - 1) * 4 / 2
step_2 = div(round(step_1), 4)
step_3 = step_2 * 4
result = rem(step_3, 3)
Atoms
Atoms are constants which value is the name
:value
[
:happy,
:this_is_an_atom,
:"this as well",
:"45"
]
WARNING!
Atoms stored in memory
Don’t create atoms dynamically!
## NO Dont!!!
String.to_atom("this a string ")
Strings & Charlist
"This is a string"
"UTF-8? Com certça meu!"
Interpolations
my_value = 10_008_000
"This an interpolated value #{inspect(my_value)}"
Please don’t abuse of this tip
my_value = [10_008_000, :a, "s"]
"This an inspected value #{inspect(my_value)}"
char_list = ~c"This is a charlist"
ASCII
?x
Actually String are binaries
<<120, 121, 122>>
And Charlist exactly lists of chars
[120, 121, 122]
[120, 121, 122] == "xyz"
[120, 121, 122] == ~c"xyz"
<<97, 98, 99>> <> <<120, 121, 122>>
multi_line_string =
"""
this is a
multiline
string,
really?
"""
IO.puts(multi_line_string)
IO.puts(char_list)
Responses from functions
-
{:ok, value}
-
:error
-
{:error, :not_found}
Tuples
my_tuple = {"a", :b, 2}
elem(my_tuple, 1)
Remember about this tuple {:ok, [value_1, value_2]}
Maps (key-value)
WARNING: avoid to mix key types
my_map =
%{
"this is a key" => :this_is_a_value,
:"atom key" => 789,
:embedded_map => %{:key => "value"},
another_key: "This is a value as well"
}
Map.fetch(my_map, "this is a key")
Map.fetch!(my_map, :"atom key")
Map.take(my_map, [:another_key, :embedded_map, :new_key])
Map.get(my_map, :some_inexistent_key, "default value if not exists")
Map.get(my_map, :embedded_map)
get_in(my_map, [:embedded_map, :key])
Map.put(my_map, :new_key, 1000)
At this moment just accept a function can be writen like
fn value -> some_process(value) end
Map.update!(my_map, :"atom key", fn value -> value * 2 end)
Map.update(my_map, :new_key, 5000, fn value -> value * 2 end)
my_map.another_key
my_map["this is a key"]
put_in(my_map, [:embedded_map, :another_key], "amazing_value")
Lists
my_list = [5, 6, 7]
Enum.into(1..20, [])
for x <- 1..10, do: x
graph LR;
A-->B;
B-->C;
C-->D;
[head | tail] = my_list
IO.puts("This a head: #{head}")
IO.inspect(tail, label: :Tail)
my_list
Lists are prepend linked list, so think always to prepend the new item and user recursion for the entire list
*remember this for pipe operator
graph LR;
id1((This is a new itme))-->A;
A-->B;
B-->C;
C-->D;
new_value = 10
[new_value | my_list]
Just remember for the future getting a Head and Tail with that syntax is because of pattern matching
That’s the reason concatenate a list & an item won’t work
my_list ++ new_value
You need to wrap it in a new list
my_list ++ [new_value]
With a large amount of data in lists you will see the performace difference
big_list = Enum.into(1..100_000_000, [])
Prepending the big_list will require to scroll all the values to connect the tail with the head of my_list
big_list ++ my_list
[1, 2, 3, 4, 5, 6, 7, 8, 9] ++ big_list
List.first(my_list)
List.last(big_list)
Enum.each(my_list, fn value -> IO.puts(value) end)
Enum.count(my_list)
Enum.empty?(my_list)
Enum.all?(my_list, fn value -> rem(value, 2) > 0 end)
Enum.all?(my_list, fn value -> value > 0 end)
Pipe Operator
initial = 1
initial
|> Kernel.+(2)
|> Kernel.+(-1)
|> Kernel.*(2)
|> Kernel./(2)
|> IO.inspect(label: :first_result_as_float)
|> round()
|> div(2)
|> IO.inspect(label: :second_result_as_integer)
|> Kernel.*(4)
|> rem(3)
what we build in real life with pipelines tents to abstract data processes and make the code readable for humans.
Transaction
|> find_the_customer
|> get_average_ticket_amount
|> maybe_discount
|> compute_total_amount