Inicio / Elixir / Elixir: Programación Funcional y Concurrente / Funciones

Funciones

Funciones anónimas, named, pipe operator y captura &.

Intermedio Funciones
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

Funciones en Elixir

Las funciones son el bloque de construcción fundamental en Elixir. Como lenguaje funcional, todo en Elixir gira alrededor de transformar datos mediante funciones. Elixir distingue entre funciones anónimas y funciones con nombre, y ofrece herramientas poderosas como el pipe operator para componer transformaciones de manera legible.

Funciones Anónimas

Las funciones anónimas se crean con la palabra clave fn y se invocan con un punto antes de los paréntesis:

# Definición y llamada
suma = fn a, b -> a + b end
suma.(3, 4)  # => 7

# Con múltiples cláusulas
saludo = fn
  "es" -> "¡Hola!"
  "en" -> "Hello!"
  "fr" -> "Bonjour!"
  _    -> "Hi!"
end

saludo.("es")  # => "¡Hola!"
saludo.("fr")  # => "Bonjour!"

# Función sin argumentos
decir_hola = fn -> "¡Hola Mundo!" end
decir_hola.()  # => "¡Hola Mundo!"

Funciones con Nombre (Named Functions)

Las funciones con nombre se definen dentro de módulos usando def (públicas) y defp (privadas):

defmodule Matematica do
  # Función pública
  def cuadrado(n), do: n * n

  # Función con múltiples cláusulas
  def factorial(0), do: 1
  def factorial(n) when n > 0 do
    n * factorial(n - 1)
  end

  # Función privada
  defp validar_positivo(n) when n >= 0, do: :ok
  defp validar_positivo(_), do: {:error, "Número negativo"}

  def raiz_cuadrada(n) do
    case validar_positivo(n) do
      :ok -> {:ok, :math.sqrt(n)}
      error -> error
    end
  end
end

Aridad

La aridad es el número de argumentos de una función. Funciones con el mismo nombre pero diferente aridad son funciones distintas:

defmodule Saludador do
  # saludar/0
  def saludar, do: "¡Hola!"

  # saludar/1
  def saludar(nombre), do: "¡Hola, #{nombre}!"

  # saludar/2
  def saludar(nombre, idioma) do
    case idioma do
      :es -> "¡Hola, #{nombre}!"
      :en -> "Hello, #{nombre}!"
    end
  end
end

Saludador.saludar()              # => "¡Hola!"
Saludador.saludar("Ana")         # => "¡Hola, Ana!"
Saludador.saludar("Ana", :en)    # => "Hello, Ana!"

Valores por Defecto

Se pueden definir valores por defecto usando \\:

defmodule Config do
  def conectar(host, puerto \\ 5432, ssl \\ false) do
    "Conectando a #{host}:#{puerto}, SSL: #{ssl}"
  end
end

Config.conectar("localhost")          # => "Conectando a localhost:5432, SSL: false"
Config.conectar("db.com", 3306)       # => "Conectando a db.com:3306, SSL: false"
Config.conectar("db.com", 3306, true) # => "Conectando a db.com:3306, SSL: true"

Closures

Las funciones anónimas capturan las variables de su entorno (closure):

multiplicador = fn factor ->
  fn numero -> numero * factor end
end

doble = multiplicador.(2)
triple = multiplicador.(3)

doble.(5)   # => 10
triple.(5)  # => 15

El Pipe Operator (|>)

El pipe operator pasa el resultado de una expresión como primer argumento de la siguiente función:

# Sin pipe (difícil de leer)
String.trim(String.downcase(String.replace("  ¡HOLA MUNDO!  ", "!", "")))

# Con pipe (legible y expresivo)
"  ¡HOLA MUNDO!  "
|> String.replace("!", "")
|> String.downcase()
|> String.trim()
# => "¡hola mundo"

# Ejemplo práctico: procesar lista de datos
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|> Enum.filter(&(rem(&1, 2) == 0))
|> Enum.map(&(&1 * &1))
|> Enum.sum()
# => 220 (4 + 16 + 36 + 64 + 100)

Operador de Captura (&)

El operador & permite crear funciones anónimas de forma concisa y referenciar funciones existentes:

# Captura de función con nombre
lista = [3, 1, 4, 1, 5]
Enum.sort(lista, &>=/2)  # => [5, 4, 3, 1, 1]

# Referencia a función de módulo
Enum.map(["hola", "mundo"], &String.upcase/1)
# => ["HOLA", "MUNDO"]

# Función anónima corta
Enum.map([1, 2, 3], &(&1 * 2))     # => [2, 4, 6]
Enum.map([1, 2, 3], &(&1 + 10))    # => [11, 12, 13]

# Con múltiples argumentos
Enum.reduce([1, 2, 3], 0, &(&1 + &2))  # => 6

# Equivalencias
fn x -> x * 2 end   # Forma larga
&(&1 * 2)            # Forma corta con captura

Funciones como Ciudadanos de Primera Clase

Las funciones pueden pasarse como argumentos, retornarse y almacenarse en estructuras de datos:

defmodule Transformador do
  def aplicar(lista, transformacion) do
    Enum.map(lista, transformacion)
  end

  def componer(f, g) do
    fn x -> f.(g.(x)) end
  end
end

duplicar = &(&1 * 2)
sumar_uno = &(&1 + 1)

Transformador.aplicar([1, 2, 3], duplicar)
# => [2, 4, 6]

duplicar_y_sumar = Transformador.componer(sumar_uno, duplicar)
duplicar_y_sumar.(5)  # => 11

Resumen

Las funciones en Elixir son herramientas versátiles que permiten definir comportamiento mediante pattern matching en cláusulas, controlar la visibilidad con funciones públicas y privadas, y componer transformaciones elegantes con el pipe operator. El operador de captura & simplifica la sintaxis en casos comunes, y los closures permiten crear funciones especializadas dinámicamente. Dominar estas herramientas es esencial para escribir código funcional expresivo.

🔒

Ejercicio práctico disponible

Funciones y el pipe operator

Desbloquear ejercicios
// Funciones y el pipe operator
// Desbloquea Pro para acceder a este ejercicio
// y ganar +50 XP al completarlo

function ejemplo() {
    // Tu código aquí...
}

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes