Diferente de linguagens tradicionais, Elixir já é capaz de fazer muito apenas fazendo pattern matching de pequenas funções com guard clauses de maneira declarativa. Além disso também fornece algumas estruturas de controle como veremos a seguir:
if condition, do: "true", else: "false"
Quando a condição é satisfeita, o corpo do if é executado, caso contrário, ele retorna nil
ou o bloco de código dentro do else
.
Forma mais verbosa:
if x > y do
"yes"
else
"no"
end
Syntax sugar:
iex> if x > y, do: :true, else: :false
unless
também aceita uma condição, porém contrário ao if
o corpo do código só executado quando a condição é falsy
unless condition, do: expression, else: expression
iex> unless false, do: :ok, else: "error"
unless something do
calc()
else
{:error, "something went wrong"}
end
Macro que permite que você faça o match de multiplas condições, algo perto de um else if
em outras linguagens.
A primeira condição que é satifesta/truthy é executada logo em seguida. Em caso de não houver nenhum match elixir dispara uma exceção/erro.
cond do
condition -> code
condition -> code
# ...
end
defmodule FizzBuzz do
def upto(number) when number > 0, do: 1..number |> Enum.map(&fizzbuzz/1)
defp fizzbuzz(term) do
cond do
rem(term, 3) == 0 and rem(term, 5) == 0 -> "FizzBuzz"
rem(term, 3) == 0 -> "Fizz"
rem(term, 5) == 0 -> "Buzz"
true -> term
end
end
end
Permite que você teste um valor em uma série de patterns e executa o código do primeiro match encontrado.
case File.open('./text.txt') do
{:ok, file} -> file
{:error, reason} -> "uh-oh"
end
Nós também podemos fazer pattern matching nas conditions.
case File.lstat "." do
{:ok, %{type: :regular}} -> "File"
{:ok, %{type: :directory}} -> "Directory"
{:error, reason} -> reason
end
defmodule ProjectFile do
def readline(path) do
case File.open(path, [:read, :utf8]) do
{:ok, file} -> IO.puts("First line: #{IO.read(file, :line)}")
{:error, reason} -> IO.puts("Failed to open file: #{reason}")
end
end
end
defmodule User do
def can_vote(person) do
case person do
person = %{age: age} when is_number(age) and age >= 18 -> true
_ -> false
end
end
end
defmodule FizzBuzz do
def upto(number) when number > 0,
do: 1..number |> Enum.map(fn n -> fizzbuzz(rem(n, 5), rem(n, 3), n) end)
defp fizzbuzz(x, y, z) do
case [x, y, z] do
[0, 0, _] -> "FizzBuzz"
[_, 0, _] -> "Fizz"
[0, _, _] -> "Buzz"
[_, _, _] -> z
end
end
end
case > case
Utilizado para combinar o matching de várias clauses ao mesmo tempo e retornar um único resultado.
cake = %{flavor: "carrot", cream_cheese: true, cook_time: 30, prep_time: 20}
with {:ok, cook_time} <- Map.fetch(cake, :cook_time),
{:ok, prep_time} <- Map.fetch(cake, :prep_time),
do: cook_time + prep_time
iex> 50
Exceptions para coisas excepcionais como quando uma database falha ou um servidor cai.
-> raise "Something bad"
<- (RuntimeError) Something bad
raise RuntimeError, message: "this will never match"
Tratamento de exceções:
try do
1 / 0
rescue
ArithmeticError -> IO.puts "Cannot divide by zero"
end
throw({:error, "something"})
exit(:failed)
Normalmente erros devem ser propagados e tratados por um supervisor, portanto exceptions não são utilizadas com muita frequência.
Docs / Referências:
- Livro - Programming Elixir 1.6
- https://elixir-lang.org/getting-started/case-cond-and-if.html
- https://elixirschool.com/en/lessons/basics/control_structures