Funciones y Módulos
Organiza tu código en funciones reutilizables y módulos bien estructurados.
Funciones anónimas
Las funciones son ciudadanos de primera clase en Elixir. Puedes crearlas, pasarlas como argumentos y retornarlas desde otras funciones:
# Crear una función anónima
iex> suma = fn a, b -> a + b end
#Function<...>
# Llamarla con punto
iex> suma.(2, 3)
5
# Sintaxis corta con &
iex> suma = &(&1 + &2)
iex> suma.(2, 3)
5
El punto (.) distingue las funciones anónimas de las funciones con nombre. suma.(1, 2) llama a una función anónima, mientras Modulo.suma(1, 2) llama a una función con nombre.
Funciones con múltiples cláusulas
saludo = fn
"Ana" -> "¡Hola Ana, bienvenida!"
"Pedro" -> "¡Qué tal Pedro!"
nombre -> "Hola, #{nombre}"
end
iex> saludo.("Ana")
"¡Hola Ana, bienvenida!"
iex> saludo.("Carlos")
"Hola, Carlos"
El operador capture (&)
# Capturar una función existente
iex> mayusculas = &String.upcase/1
iex> mayusculas.("hola")
"HOLA"
# Sintaxis corta para funciones simples
iex> doble = &(&1 * 2)
iex> doble.(5)
10
# &1, &2, etc. son los parámetros
iex> concatenar = &("#{&1} y #{&2}")
iex> concatenar.("A", "B")
"A y B"
Módulos
Los módulos agrupan funciones relacionadas:
defmodule Matematica do
def suma(a, b) do
a + b
end
def resta(a, b) do
a - b
end
# Sintaxis corta para funciones de una línea
def doble(n), do: n * 2
end
iex> Matematica.suma(1, 2)
3
iex> Matematica.doble(5)
10
Funciones públicas vs privadas
defmodule Saludo do
# Función pública (def)
def hola(nombre) do
formatear("Hola", nombre)
end
# Función privada (defp) - solo accesible dentro del módulo
defp formatear(saludo, nombre) do
"#{saludo}, #{nombre}!"
end
end
iex> Saludo.hola("Ana")
"Hola, Ana!"
iex> Saludo.formatear("Hola", "Ana")
** (UndefinedFunctionError) function Saludo.formatear/2 is undefined or private
Aridad de funciones
La aridad es el número de argumentos. Funciones con el mismo nombre pero diferente aridad son funciones diferentes:
defmodule Ejemplo do
def saludo, do: "Hola" # saludo/0
def saludo(nombre), do: "Hola, #{nombre}" # saludo/1
def saludo(saludo, nombre), do: "#{saludo}, #{nombre}" # saludo/2
end
iex> Ejemplo.saludo()
"Hola"
iex> Ejemplo.saludo("Ana")
"Hola, Ana"
iex> Ejemplo.saludo("Buenos días", "Ana")
"Buenos días, Ana"
Valores por defecto
defmodule Saludo do
def hola(nombre, saludo \\ "Hola") do
"#{saludo}, #{nombre}!"
end
end
iex> Saludo.hola("Ana")
"Hola, Ana!"
iex> Saludo.hola("Ana", "Buenos días")
"Buenos días, Ana!"
Guards (Guardas)
Los guards añaden condiciones adicionales al pattern matching:
defmodule Clasificador do
def tipo(n) when is_integer(n) and n < 0 do
:negativo
end
def tipo(0), do: :cero
def tipo(n) when is_integer(n) and n > 0 do
:positivo
end
def tipo(_), do: :no_es_entero
end
iex> Clasificador.tipo(-5)
:negativo
iex> Clasificador.tipo(0)
:cero
iex> Clasificador.tipo(10)
:positivo
iex> Clasificador.tipo("texto")
:no_es_entero
Funciones permitidas en guards
| Categoría | Funciones |
|---|---|
| Comparación | ==, !=, ===, !==, <, >, <=, >= |
| Booleanos | and, or, not |
| Aritméticos | +, -, *, / |
| Tipo | is_atom, is_binary, is_integer, is_float, is_list, is_map, is_nil, is_number, is_tuple |
| Otros | abs, div, rem, length, map_size, tuple_size |
El operador pipe (|>)
El pipe pasa el resultado de una expresión como primer argumento de la siguiente:
# Sin pipe (difícil de leer)
resultado = String.split(String.trim(String.downcase(" HOLA MUNDO ")))
# Con pipe (flujo claro de datos)
resultado = " HOLA MUNDO "
|> String.downcase()
|> String.trim()
|> String.split()
# resultado: ["hola", "mundo"]
Usa el pipe para expresar transformaciones de datos de forma clara. Cada paso debe ser una transformación simple y el flujo debe leerse de arriba a abajo.
Ejemplo práctico
defmodule Procesador do
def procesar_nombres(texto) do
texto
|> String.split(",") # ["ana", " pedro ", "MARIA"]
|> Enum.map(&String.trim/1) # ["ana", "pedro", "MARIA"]
|> Enum.map(&String.capitalize/1) # ["Ana", "Pedro", "Maria"]
|> Enum.sort() # ["Ana", "Maria", "Pedro"]
end
end
iex> Procesador.procesar_nombres("ana, pedro , MARIA")
["Ana", "Maria", "Pedro"]
Módulos anidados
defmodule MiApp.Usuarios.Validador do
def validar_email(email) do
String.contains?(email, "@")
end
end
# Equivalente a:
defmodule MiApp do
defmodule Usuarios do
defmodule Validador do
def validar_email(email) do
String.contains?(email, "@")
end
end
end
end
Atributos de módulo
defmodule Configuracion do
# Constantes (evaluadas en compilación)
@version "1.0.0"
@autor "Tu Nombre"
def version, do: @version
def info, do: "#{@autor} - v#{@version}"
end
iex> Configuracion.version()
"1.0.0"
Usa @doc y @moduledoc para documentar tu código:
defmodule MiModulo do
@moduledoc """
Este módulo hace cosas increíbles.
"""
@doc """
Suma dos números.
## Ejemplos
iex> MiModulo.suma(1, 2)
3
"""
def suma(a, b), do: a + b
end
Crea un módulo StringUtils con las siguientes funciones:
reverso/1- invierte un stringpalabras/1- cuenta las palabras en un stringtruncar/2- trunca un string a n caracteres, añadiendo "..." si es necesario
Crea una función que tome una lista de números y usando pipes:
- Filtre solo los positivos
- Los duplique
- Los ordene de mayor a menor
- Tome solo los primeros 3