Elm es conocido por su promesa audaz: “sin errores en tiempo de ejecución”.
Pero, ¿cómo logra mantener esa pureza funcional incluso cuando necesita interactuar con el mundo real —hacer peticiones HTTP, leer el tiempo o escuchar eventos del navegador?
La respuesta está en su sistema de efectos controlados, manejados a través de tres conceptos clave:
- Task
- Cmd (Command)
- Sub (Subscription)
En lenguajes imperativos, un efecto secundario (como imprimir en consola, hacer un fetch, o leer el reloj) puede suceder en cualquier parte del código.
En Elm, en cambio, toda función debe ser pura: dado el mismo input, siempre devuelve el mismo output.
Esto significa que no podés ejecutar efectos directamente dentro de tus funciones —en su lugar, los describís y Elm se encarga de ejecutarlos de forma controlada.
Un Cmd (Command) representa una acción que Elm debe realizar fuera del mundo puro, y que luego generará un mensaje (msg) cuando termine.
Ejemplo: hacer una solicitud HTTP.
import Http
import Json.Decode exposing (string)
type Msg
= GotGreeting (Result Http.Error String)
fetchGreeting : Cmd Msg
fetchGreeting =
Http.get
{ url = "https://api.example.com/hello"
, expect = Http.expectString GotGreeting
}
Este Cmd Msg no ejecuta la solicitud, solo le dice al runtime de Elm:
Por favor, hacé esta petición y cuando tengas el resultado, mandame un GotGreeting.
En tu update, procesás el resultado:
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
GotGreeting (Ok text) ->
({ model | greeting = text }, Cmd.none)
GotGreeting (Err _) ->
({ model | greeting = "Error al conectar" }, Cmd.none)
Task: trabajos que pueden fallar o devolver un valor
Un Task es una descripción más general de un efecto que puede producir un resultado o un error.
Por ejemplo, si quisieras obtener el tiempo:
import Task
import Time exposing (Posix)
type Msg
= GotTime Posix
getTime : Cmd Msg
getTime =
Task.perform GotTime Time.now
Task.perform convierte un Task en un Cmd, para que pueda ser ejecutado por Elm.
El flujo sería:
- Time.now devuelve un Task Never Posix (una tarea que no falla y produce un tiempo).
- Task.perform lo transforma en un Cmd Msg.
- Elm ejecuta el comando y manda GotTime cuando termina.
Sub msg: escuchar eventos externos
Mientras que Cmd representa acciones que Elm inicia,
Sub representa cosas que suceden fuera y Elm escucha.
Por ejemplo, escuchar el paso del tiempo:
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every 1000 Tick
Cada segundo, Elm enviará un mensaje Tick, que procesás en tu update:
type Msg
= Tick Posix
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Tick time ->
({ model | currentTime = time }, Cmd.none)
Elm logra un equilibrio brillante:
- mantiene la pureza funcional del lenguaje,
- pero sin renunciar al mundo real.
Gracias a Task, Cmd y Sub, todo efecto está tipado, controlado y predecible, lo que permite que el compilador te ayude a manejar cada posible resultado.
Por eso, en Elm, incluso el caos del mundo exterior se maneja con elegancia funcional.
