Gleam vs Elixir: cuándo elegir uno u otro en el ecosistema BEAM

Gleam y Elixir comparten la misma máquina virtual (BEAM), el mismo gestor de paquetes (Hex) y muchos de los mismos objetivos: código concurrent, tolerante a fallos y pensado para sistemas distribuidos. Pero las diferencias entre los dos son suficientemente importantes como para que la elección entre uno y otro no sea trivial.

Este artículo no intenta decir que uno es mejor que el otro. Son herramientas distintas con puntos fuertes distintos, y entender esas diferencias es la base para elegir bien.

La diferencia fundamental: tipos estáticos vs dinámicos

Elixir es dinámicamente tipado. Los tipos se comprueban en tiempo de ejecución, no antes. Esto da mucha flexibilidad a la hora de escribir código, pero significa que ciertos errores solo aparecen cuando el código ya está corriendo en producción. Dialyzer puede ayudar a detectar algunos problemas de tipos por análisis estático, pero no es un verificador completo.

Gleam tiene tipado estático con inferencia completa. El compilador verifica los tipos antes de generar ningún código. Si hay un error de tipos, el build falla y te dice exactamente dónde. No hay Dialyzer, no hay anotaciones opcionales que puedes ignorar: los tipos son parte del lenguaje desde el principio.

// En Gleam esto no compila:
// fn suma(a: Int, b: String) -> Int { a + b }
// Error: Type mismatch - expected Int, got String

// Tienes que ser coherente:
fn suma(a: Int, b: Int) -> Int { a + b }

Sintaxis: dos estilos diferentes

Elixir tiene una sintaxis expresiva con macros, protocolos, y mucha metaprogramación. El lenguaje es muy flexible y puedes hacer cosas muy sofisticadas con las macros de compilación. Phoenix y Ecto, por ejemplo, usan macros extensivamente para su DSL.

Gleam es deliberadamente más simple. No tiene macros, no tiene metaprogramación, no tiene herencia. La sintaxis recuerda más a Rust o a un ML. Lo que ves es lo que hay:

// Gleam: función con pipe operator
pub fn process(input: String) -> String {
  input
  |> string.trim
  |> string.uppercase
  |> string.replace(" ", "_")
}
# Elixir: equivalente con pipe
def process(input) do
  input
  |> String.trim()
  |> String.upcase()
  |> String.replace(" ", "_")
end

El pipe operator es prácticamente igual en los dos. Las diferencias empiezan cuando la lógica se complica.

Ecosistema y madurez

Elixir tiene un ecosistema muy maduro. Phoenix es uno de los frameworks web más completos que existen. Ecto es un ORM/query builder sólido. Hay miles de paquetes en Hex, documentación abundante y una comunidad grande con años de experiencia en producción.

Gleam es mucho más joven. Alcanzó la versión 1.0 en marzo de 2024, así que el ecosistema propio todavía está construyéndose. Wisp es el framework HTTP principal, pero no tiene la madurez de Phoenix. La buena noticia es que Gleam puede usar librerías de Erlang y, con algo de trabajo de interop, también de Elixir. El acceso al ecosistema BEAM completo está ahí, aunque con más fricción que en Elixir.

Manejo de errores

Elixir usa excepciones para los errores inesperados (aunque la filosofía "let it crash" del BEAM significa que tampoco se abusan). Para errores esperados, el patrón {:ok, value} y {:error, reason} es la convención, pero no está forzado por el lenguaje.

En Gleam no hay excepciones. El tipo Result(ok, err) es la única forma de representar operaciones que pueden fallar, y el compilador obliga a manejarlo. No puedes ignorar un error aunque quieras:

import gleam/result

pub fn main() {
  let resultado = parse_number("abc")
  // No puedes usar resultado sin manejar el caso Error
  case resultado {
    Ok(n)    -> io.println("Número: " <> int.to_string(n))
    Error(e) -> io.println("Error: " <> e)
  }
}

Target JavaScript

Elixir solo compila a BEAM. Para JavaScript necesitas Liveview (que mantiene la lógica en el servidor) o usar otro lenguaje para el frontend.

Gleam puede compilar tanto a BEAM como a JavaScript. Puedes escribir lógica compartida en Gleam y usarla tanto en el servidor como en el frontend. Esto es algo que Elixir no puede hacer de forma nativa.

Tabla comparativa rápida

Característica

Gleam

Elixir

Sistema de tipos

Estático, inferencia completa

Dinámico (Dialyzer opcional)

Macros / metaprogramación

No

Sí, extensivas

Target JS

Sí (nativo)

No

Framework web

Wisp (joven)

Phoenix (maduro)

Versión estable desde

Marzo 2024

2014

Excepciones

No (Result)

Interop Erlang

Sí (@external)

Sí (nativo)

Cuándo elegir Gleam

Gleam tiene sentido cuando los errores de tipos son un problema real en tu proyecto, cuando el equipo viene de lenguajes tipados estáticamente (Rust, TypeScript, Haskell) y cuando quieres usar el mismo lenguaje para servidor y frontend con JavaScript como target adicional.

Cuándo elegir Elixir

Elixir es la opción más segura si necesitas un ecosistema maduro ahora mismo, si Phoenix es importante para tu proyecto (LiveView, Channels, PubSub), o si el equipo ya tiene experiencia en él. La comunidad y los recursos disponibles son considerablemente mayores.

Lo que ambos tienen en común es lo que importa para sistemas concurrentes: la BEAM. El modelo de actores, los supervisores de OTP y la tolerancia a fallos son idénticos en los dos. La elección entre Gleam y Elixir es una elección sobre el sistema de tipos y el ecosistema, no sobre la plataforma de ejecución.

Imagen: Pexels / Digital Buggu

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP