Translate

domingo, 26 de octubre de 2025

Manejo de efectos en Elm: Tasks, Commands y Subscriptions


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:

  1. Time.now devuelve un Task Never Posix (una tarea que no falla y produce un tiempo).
  2. Task.perform lo transforma en un Cmd Msg.
  3. 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.