Skip to content

Instantly share code, notes, and snippets.

@jeferson-sb
Last active April 7, 2022 20:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeferson-sb/f6c26acaa45b16708f2b275063859145 to your computer and use it in GitHub Desktop.
Save jeferson-sb/f6c26acaa45b16708f2b275063859145 to your computer and use it in GitHub Desktop.
Elixir - Control Flow

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 and unless

if

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

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

cond

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

Example

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

case

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

Examples

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

Bonus

with

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

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:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment