Funciones
En Elixir y muchos lenguajes funcionales, las funciones son ciudadanos de primera clase.
Funciones Anónimas
Tal como su nombre lo indica, una función anónima no tiene nombre. Como vimos en la lección de Enum, éstos se pasan con frecuencia a otras funciones. Para definir una función anónima en Elixir necesitamos las palabras clave fn y end. Dentro de éstos podemos definir cualquier número de parámetros y cuerpos funcionales separados por ->
iex> sum = fn (a, b) -> a + b end
iex> sum.(2, 3)
5
El operador &
El uso de funciones anónimas es una práctica común en Elixir hay una forma abreviada de hacerlo:
iex> sum = &(&1 + &2)
iex> sum.(2, 3)
5
Como probablemente ya se supone, en la versión abreviada nuestros parámetros están disponibles para nosotros como & 1, & 2, & 3, y así sucesivamente.
Pattern Matching
Pattern matching no esta limitado solo a variables sino que tambien se pueden utilizar en funciones.
Se pueden utilizar pattern matching con el primer conjunto de parámetros que coinciden e invoca el cuerpo correspondiente:
iex> handle_result = fn
...> {:ok, result} -> IO.puts "Handling result..."
...> {:error} -> IO.puts "An error has occurred!"
...> end
iex> some_result = 1
iex> handle_result.({:ok, some_result})
Handling result...
iex> handle_result.({:error})
An error has occurred!
Funciones con nombre
Podemos definir funciones con nombre utilizando la palabra clave def. Las funciones definidas dentro de un modulo pueden ser accedidas por otros módulos:
defmodule Greeter do
def hello(name) do
"Hello, " <> name
end
end
iex> Greeter.hello("Sean")
"Hello, Sean"
Si queremos definir una función en una linea podemos usar do:
defmodule Greeter do
def hello(name), do: "Hello, " <> name
end
Y podemos utilizar pattern matching:
defmodule Length do
def of([]), do: 0
def of([_ | tail]), do: 1 + of(tail)
end
iex> Length.of []
0
iex> Length.of [1, 2, 3]
3
Funciones con nombre y número de argumentos
Como el lenguaje es de tipado débil, la firma de la función esta dada por el nombre de la función y la cantidad de parámetros. Es decir las funciones aceptan sobre carga y esta dada por el numero de parametros:
defmodule Greeter2 do
def hello(), do: "Hello, anonymous person!" # hello/0
def hello(name), do: "Hello, " <> name # hello/1
def hello(name1, name2), do: "Hello, #{name1} and #{name2}"
# hello/2
end
A diferencia de otros lenguajes, la firma de una función esta dada por el nombre y el numero de parámetros.
Funciones privadas
Una función privada solo se puede llamar dentro del modulo y se define con defp :
defmodule Greeter do
def hello(name), do: phrase <> name
defp phrase, do: "Hello, "
end
iex> Greeter.hello("Sean")
"Hello, Sean"
iex> Greeter.phrase
** (UndefinedFunctionError) undefined function: Greeter.phrase/0
Greeter.phrase()
Guards
En el ejemplo siguiente tenemos dos funciones con la misma firma, nos basamos en guards para determinar qué utilizar en función del tipo del argumento:
defmodule Greeter do
def hello(names) when is_list(names) do
names
|> Enum.join(", ")
|> hello
end
def hello(name) when is_binary(name) do
phrase() <> name
end
defp phrase, do: "Hello, "
end
iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"
Argumentos por defecto
Se pueden utilizar argumentos por defecto:
defmodule Greeter do
def hello(name, language_code \\ "en") do
phrase(language_code) <> name
end
defp phrase("en"), do: "Hello, "
defp phrase("es"), do: "Hola, "
end
iex> Greeter.hello("Sean", "en")
"Hello, Sean"
iex> Greeter.hello("Sean")
"Hello, Sean"
iex> Greeter.hello("Sean", "es")
"Hola, Sean"
Cuando combinamos nuestro ejemplo de guard con argumentos predeterminados, nos topamos con un problema. Veamos cómo podría verse:
defmodule Greeter do
def hello(names, language_code \\ "en") when is_list(names) do
names
|> Enum.join(", ")
|> hello(language_code)
end
def hello(name, language_code \\ "en") when is_binary(name) do
phrase(language_code) <> name
end
defp phrase("en"), do: "Hello, "
defp phrase("es"), do: "Hola, "
end
** (CompileError) def hello/2 has default values and multiple clauses, define a function head with the defaults
Elixir no le gusta los argumentos por defecto en múltiples funciones de coincidencia, puede ser confuso. Para manejar esto agregamos una cabeza de función con nuestros argumentos por defecto:
defmodule Greeter do
def hello(names, language_code \\ "en")
def hello(names, language_code) when is_list(names) do
names
|> Enum.join(", ")
|> hello(language_code)
end
def hello(name, language_code) when is_binary(name) do
phrase(language_code) <> name
end
defp phrase("en"), do: "Hello, "
defp phrase("es"), do: "Hola, "
end
iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"
iex> Greeter.hello ["Sean", "Steve"], "es"
"Hola, Sean, Steve"
Dejo link:
https://elixirschool.com/en/lessons/basics/functions/