jueves, 22 de junio de 2017

Empezando con Elixir 5


Estructuras de control

if y unless

Si hemos programado estamos familiarizados con el if y en menor medida con el unless. Estas estructuras son construidas en elixir por medio de macros y no forma parte del lenguaje.

Por otra parte es necesario aclarar que en elixir solo es falso el nil y el false (por supuesto)

iex> if String.valid?("Hello") do
...>   "Valid string!"
...> else
...>   "Invalid string."
...> end
"Valid string!"

iex> if "a string value" do
...>   "Truthy"
...> end
"Truthy"

Se utiliza unless para analizar los casos falsos:

iex> unless is_integer("hello") do
...>   "Not an Int"
...> end
"Not an Int"

case

Esta estructura nos permite utilizar el pattern matching:

iex> case {:ok, "Hello World"} do
...>   {:ok, result} -> result
...>   {:error} -> "Uh oh!"
...>   _ -> "Catch all"
...> end

"Hello World"

El valor _ es similar al default de c o java. Y esto puede servir para prevenir errores:

iex> case :even do
...>   :odd -> "Odd"
...> end
** (CaseClauseError) no case clause matching: :even

iex> case :even do
...>   :odd -> "Odd"
...>   _ -> "Not Odd"
...> end
"Not Odd"

Dado que case se basa en pattern matching, se aplican todas las mismas reglas y restricciones. Si se quiere que coincida con variables existentes debe usar el operador pin ^ :

iex> pie = 3.14
 3.14
iex> case "cherry pie" do
...>   ^pie -> "Not so tasty"
...>   pie -> "I bet #{pie} is tasty"
...> end
"I bet cherry pie is tasty"

Permite tambien utilizar clausulas de protección:

iex> case {1, 2, 3} do
...>   {1, x, 3} when x > 0 ->
...>     "Will match"
...>   _ ->
...>     "Won't match"
...> end
"Will match"

cond

Cuando necesitamos hacer coincidir condiciones en vez de valores, podemos recurrir a cond.  Esto es similar a else if o elsif de otros lenguajes:

iex> cond do
...>   2 + 2 == 5 ->
...>     "This will not be true"
...>   2 * 2 == 3 ->
...>     "Nor this"
...>   1 + 1 == 2 ->
...>     "But this will"
...> end
"But this will"

Igual que case podemos tener un default que en este caso va ser el valor true :

iex> cond do
...>   7 + 1 == 0 -> "Incorrect"
...>   true -> "Catch all"
...> end
"Catch all"

with

with es útil cuando se puede usar una sentencia case anidada o situaciones que no se pueden canalizar de forma limpia. La expresión with se compone de las palabras clave, los generadores y, finalmente, una expresión.

Veamos un ejemplo simple y luego veremos algo más:

iex> user = %{first: "Sean", last: "Callan"}
%{first: "Sean", last: "Callan"}
iex> with {:ok, first} <- Map.fetch(user, :first),
...>      {:ok, last} <- Map.fetch(user, :last),
...>      do: last <> ", " <> first
"Callan, Sean"

En el caso de que una expresión no coincida, se devolverá un error:

iex> user = %{first: "doomspork"}
%{first: "doomspork"}
iex> with {:ok, first} <- Map.fetch(user, :first),
...>      {:ok, last} <- Map.fetch(user, :last),
...>      do: last <> ", " <> first
:error

Ahora veamos un ejemplo más grande sin with  y luego veamos cómo podemos refactorizarlo:

case Repo.insert(changeset) do
  {:ok, user} ->
    case Guardian.encode_and_sign(resource, :token, claims) do
      {:ok, token, full_claims} ->
        important_stuff(token, full_claims)
      error -> error
    end
  error -> error
end

Ahora usaremos with:

with {:ok, user} <- Repo.insert(changeset),
     {:ok, token, full_claims} <- Guardian.encode_and_sign(user, :token) do
  important_stuff(token, full_claims)
end

En Elixir 1.3 el with soporta el else:

import Integer

m = %{a: 1, c: 3}

a =
  with {:ok, number} <- Map.fetch(m, :a),
    true <- Integer.is_even(number) do
      IO.puts "#{number} divided by 2 is #{div(number, 2)}"
      :even
  else
    :error ->
      IO.puts "We don't have this item in map"
      :error
    _ ->
      IO.puts "It is odd"
      :odd
  end

Dejo link: https://elixirschool.com/lessons/basics/control-structures/