Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us
Notesclub

Flow control

class1/flow_control.livemd

Flow control

Match operator =

Matching operator = is used in one of 3 ways:

  • to assign value to variable
  • to check if the value matches given pattern, pin operator ^ is useful when you want to check pattern without reassigning variable
  • to extract data from more complex structures

see more here.

x = 1
# x = 2
# x
# ^x = 2
{:ok, value} = {:ok, :success}
value
list = [1, 2, 3]
# [head | tail] = list
# head
# tail
# [^head, 2, b] = list
# b
# [a, b] = list
# [] = list
{:error, %{:reason => reason}} = {:error, %{:reason => "something went wong", :code => 123}}
reason

If / unless

if true do
  IO.puts("This works!")
end

# unless true do
#  IO.puts("This will never be seen")
# end

# unless false do
#   IO.puts("This will be seen")
# end

# if nil do
#  IO.puts("This won't be seen")
# else
#  IO.puts("This will")
# end

Case

earnings = 10

special_value = 12

case earnings do
  10 -> "you earn average salary"
  # pin operator
  ^special_value -> "you earn #{special_value}"
  salary when salary > 12 -> "you earn #{salary} you are too rich"
  _ -> "you are underpaid"
end

Cond

case is useful when you need to match against different values. However, in many circumstances, we want to check different conditions and find the first one that does not evaluate to nil or false. If all of the conditions return nil or false, an error (CondClauseError) is raised. For this reason, it may be necessary to add a final condition, equal to true, which will always match

cond do
  2 + 2 == 5 ->
    "This will not be true"

  2 * 2 == 3 ->
    "Nor this"

  1 + 1 == 2 ->
    "But this will"
end
cond do
  2 + 2 == 5 ->
    "This is never true"

  2 * 2 == 3 ->
    "Nor this"

  true ->
    "This is always true (equivalent to else)"
end

Anonymous functions

An anonymous function has no name. They allow us to store and pass executable code around as it was an integer or string. They are delimited by the keywords fn and end. To invoke anonymous funtion use sytnax fun.(args...)

add = fn a, b -> a + b end
add.(2, 3)
# add_two = fn a -> add.(a, 2) end
# add_two.(5)
# shorthand notation
add2 = &(&1 + &2)
add2.(2, 3)
# Pattern meatching in annonimous functions:
count = fn
  1 -> :one
  2 -> :two
  _ -> :many
end

[
  count.(1),
  count.(2),
  count.(3),
  count.(4)
]

Pipe operator

The |> symbol used in the snippet above is the pipe operator: it takes the output from the expression on its left side and passes it as the first argument to the function call on its right side. It’s similar to the Unix | operator. Its purpose is to highlight the data being transformed by a series of functions.

add.(add.(1, 2), 3)
# 1
# |> add.(2)
# |> add.(3)

Higher order functions

Functions that accept orther functions as arguments. Read more here

devices = [
  %{:os => :mac, :processor => :intel, :type => :laptop},
  %{:os => :mac, :processor => :intel, :type => :phone},
  %{:os => :windows, :processor => :intel, :type => :laptop},
  %{:os => :android, :processor => :intel, :type => :phone},
  %{:os => :windows, :processor => :intel, :type => :phone}
]

## I want to know what type of divices are used  
# devices
# |> Enum.map(fn %{:type => type} -> type end)
# |> Enum.uniq()

## I want to see the windows running divices
# devices
# |> Enum.filter(fn(%{:os => :windows}) -> true; _ -> false end)

See also

Modules

When writing Elixir code we use functions. And we put group of functions into modules.

There are 2 types of functions that we put inside modules:

  • public - can only be accessed from anywhere in the code, defined with def
  • private - can only be accessed within module, defined with defp

Function always return last value it evaluated

defmodule Math.Examples.Fib do
  def fib(0), do: 1
  def fib(1), do: 1

  def fib(n) do
    fib(n - 1) + fib(n - 2)
  end

  def seq_fib(max), do: piv_fib(max, 0, 1, 0)

  defp piv_fib(max, counter, current, previous) when counter >= max, do: current

  ## Handling the warining:
  ## warning: variable "previous" is unused (if the variable is not meant to be used, prefix it with an underscore)
  ## first_classes/basic_constructs.livemd#cell:11: Fibbonaci.Sequence.piv_fib/4

  # defp piv_fib(max, counter, current, _previous) when counter > max, do: current
  # defp piv_fib(max, counter, current, _) when counter > max, do: current

  defp piv_fib(max, counter, current, previous) do
    piv_fib(max, counter + 1, current + previous, current)
  end
end

defmodule Math.Examples.Add do
  def add(a, b) do
    a + b
    ## When you uncomment `:ok` it will be returned instead of a + b
    # :ok
  end
end
{[
   Math.Examples.Fib.fib(0),
   Math.Examples.Fib.fib(1),
   Math.Examples.Fib.fib(2),
   Math.Examples.Fib.fib(3),
   Math.Examples.Fib.fib(4),
   Math.Examples.Fib.fib(5)
 ],
 [
   Math.Examples.Fib.seq_fib(0),
   Math.Examples.Fib.seq_fib(1),
   Math.Examples.Fib.seq_fib(2),
   Math.Examples.Fib.seq_fib(3),
   Math.Examples.Fib.seq_fib(4),
   Math.Examples.Fib.seq_fib(5)
 ]}
Math.Examples.Add.add(2, 3)