Translate

Mostrando las entradas con la etiqueta elm. Mostrar todas las entradas
Mostrando las entradas con la etiqueta elm. Mostrar todas las entradas

miércoles, 12 de noviembre de 2025

Elm vs Haskell: dos caminos del paradigma funcional


Haskell y Elm comparten una raíz común: ambos son lenguajes funcionales puros, con tipado estático, inferencia de tipos y un fuerte énfasis en la inmutabilidad.

Sin embargo, cada uno tomó un camino distinto.

Haskell apostó por la abstracción, la teoría de tipos avanzada y la expresividad; Elm, por la simplicidad, la seguridad y la experiencia del desarrollador.

Haskell nació en el ámbito académico, con el objetivo de ser un lenguaje funcional puro que sirviera como base de investigación.

Por eso prioriza la expresividad, la abstracción y la corrección formal.

Su lema podría ser: “todo puede expresarse en tipos”.

Elm, en cambio, nació del mundo web.

Su meta no es la investigación, sino la confiabilidad.

Fue diseñado para construir interfaces web sin errores en tiempo de ejecución.

Su lema podría ser: “ningún runtime exception, nunca”.


En resumen:

Haskell es una herramienta para explorar los límites del paradigma funcional.

Elm es una herramienta para aplicar ese paradigma con seguridad y pragmatismo.


Ambos tienen tipado estático e inferencia, pero el sistema de tipos de Haskell es mucho más poderoso.

Haskell permite type classes, kind polymorphism, type families, GADTs, monads, existentials y un sinfín de extensiones.

Elm tiene un sistema de tipos mucho más pequeño, pero más legible y predecible.

En Haskell, el tipo puede expresar conceptos avanzados:


fmap :: Functor f => (a -> b) -> f a -> f b


Mientras que en Elm, los tipos se mantienen simples y directos:


List.map : (a -> b) -> List a -> List b


En Haskell podés crear tus propias type classes (Eq, Ord, Monad, etc.).

En Elm no existen type classes: sólo hay un conjunto fijo de restricciones (number, comparable, appendable).


Haskell es un lenguaje donde los tipos son una herramienta de abstracción.

Elm los usa más bien como una herramienta de seguridad.


Tanto Haskell como Elm son lenguajes funcionales puros: ninguna función puede tener efectos secundarios sin declararlo explícitamente.

Pero los abordan de forma distinta.


Haskell utiliza el sistema de monads para modelar efectos: IO, Maybe, State, etc.

Cada efecto se encapsula en un tipo, y se encadenan usando do notation.


main :: IO ()

main = do

    name <- getLine

    putStrLn ("Hola, " ++ name)


Elm evita completamente las monads.

En su lugar, usa un modelo explícito de efectos: los Commands (Cmd) y Subscriptions (Sub), que forman parte del Elm Architecture.

Esto mantiene la pureza del lenguaje sin exponer al programador a conceptos teóricos complejos.


update : Msg -> Model -> (Model, Cmd Msg)

update msg model =

    case msg of

        CargarDatos ->

            ( model, Http.get {...} )


        DatosCargados datos ->

            ( { model | items = datos }, Cmd.none )


Haskell ofrece poder; Elm ofrece control.

En Haskell, el manejo de efectos es flexible y extensible.

En Elm, es seguro y predecible.


Elm está diseñado para ser simple y consistente.

La sintaxis es limpia, sin operadores ambiguos ni extensiones opcionales.

Haskell, en cambio, permite una gran expresividad, pero también puede resultar críptico.


En Elm:


sumar : Int -> Int -> Int

sumar x y =

    x + y


En Haskell:


sumar :: Int -> Int -> Int

sumar x y = x + y


Parecen casi iguales, pero Haskell permite redefinir operadores, crear infix personalizados o usar point-free style, lo que puede aumentar la complejidad.

Elm evita deliberadamente esa flexibilidad para mantener el código legible para todos.


Haskell es un lenguaje generalista: se usa en compiladores, sistemas financieros, backends web, análisis estático y más.

Su ecosistema es vasto y diverso, aunque muchas librerías varían en calidad y mantenimiento.


Elm se centra exclusivamente en el frontend web.

Todo su diseño gira en torno a construir aplicaciones en el navegador.

No hay ambigüedad: un proyecto Elm siempre es una aplicación web.

A cambio de esa limitación, ofrece una experiencia coherente, con un compilador extremadamente útil y mensajes de error ejemplares.


La diferencia más grande entre ambos lenguajes quizá sea emocional. Haskell a veces puede parecer un rompecabezas: poderoso, elegante, pero con una curva de aprendizaje pronunciada.

Elm, en cambio, busca que programar sea agradable, incluso para quienes no tienen experiencia previa en programación funcional.

El compilador de Elm no sólo te dice qué está mal, sino cómo arreglarlo.

El de Haskell, aunque más sofisticado, puede ser más críptico si no conocés sus fundamentos teóricos.


Haskell y Elm son dos lenguajes que muestran dos filosofías complementarias del mundo funcional.

Haskell te da un universo para explorar la abstracción.

Elm te da un terreno firme donde construir sin errores.

martes, 11 de noviembre de 2025

Type constraints en Elm


Elm es un lenguaje de tipado estático e inferencia fuerte: el compilador deduce los tipos automáticamente, pero también te permite declarar funciones genéricas que funcionan con más de un tipo.

Por ejemplo:


identity : a -> a

identity x = x


Esta función acepta cualquier tipo a.

Sin embargo, a veces queremos restringir qué tipos son válidos.

Ahí entran en juego los type constraints (restricciones de tipo).

En Elm, los type constraints permiten decir:

> “Este tipo genérico debe cumplir con ciertas propiedades (por ejemplo, ser comparable o numérico)”.


A diferencia de Haskell o Scala, Elm no tiene type classes, pero ofrece un pequeño conjunto de restricciones integradas que cubren los casos más comunes.

Elm define cuatro categorías de tipos con restricciones que podés usar en tus firmas de tipo:

  • number: Tipos que soportan operaciones aritméticas como Int, Float
  • comparable: Tipos que pueden ordenarse o compararse  como Int, Float, Char, String, tuples de comparables
  • appendable: Tipos que pueden concatenarse como String, List a
  • compappend:| Tipos que son a la vez comparable y appendable 


Podés restringir una función a operar solo sobre números:


sumar : number -> number -> number

sumar x y =

    x + y


Esto funciona con Int o Float, pero no con String.


Si necesitás ordenar o comparar valores:


menor : comparable -> comparable -> comparable

menor a b =

    if a < b then

        a

    else

        b


O incluso:


ordenar : List comparable -> List comparable

ordenar lista =

    List.sort lista


Cuando querés concatenar elementos:


concatenar : appendable -> appendable -> appendable

concatenar a b =

    a ++ b


Funciona con:


concatenar "Hola, " "mundo!"        -- "Hola, mundo!"

concatenar [1,2] [3,4]              -- [1,2,3,4]


En Elm no se pueden definir tipos personalizados que sean comparable o appendable.

Por ejemplo, este tipo:


type alias Persona =

    { nombre : String, edad : Int }


No puede usarse en una función List.sort directamente.

Pero podés ordenarlo con una clave usando List.sortBy:


ordenarPorEdad : List Persona -> List Persona

ordenarPorEdad personas =

    List.sortBy .edad personas


O definir un criterio personalizado:


ordenarPorNombreDesc : List Persona -> List Persona

ordenarPorNombreDesc personas =

    List.sortWith (\a b -> compare b.nombre a.nombre) personas


Elm mantiene su sistema de tipos simple pero poderoso: no hay typeclasses ni herencia, pero sí restricciones útiles y seguras para los casos más comunes.


sábado, 1 de noviembre de 2025

Diseñando interfaces declarativas con Elm UI


Elm es conocido por su modelo funcional puro y su arquitectura confiable (el Elm Architecture).

Sin embargo, muchos desarrolladores se frustran al escribir HTML y CSS en el código de Elm tradicional.

Ahí entra Elm UI, una librería creada por Matthew Griffith que propone algo radical:

“Construir interfaces sin HTML ni CSS.”

mdgriffith/elm-ui reemplaza los módulos Html y Html.Attributes por un conjunto de tipos y funciones que representan elementos visuales y propiedades de diseño.

En vez de mezclar markup y estilo, describís la interfaz de forma estructurada y tipada.


Por ejemplo:


import Element exposing (..)

import Element.Font as Font


main =

    layout [] <|

        column [ spacing 20, centerX, centerY ]

            [ el [ Font.bold ] (text "Hola Elm UI!")

            , el [] (text "Una forma diferente de pensar las interfaces.")

            ]


Este código produce una interfaz centrada con dos textos.

Sin CSS, sin HTML — todo se define a través de funciones puras.


Elm UI se basa en unos pocos conceptos muy consistentes:

Element: Unidad visual principal, equivalente a un “nodo” del DOM

layout: Punto de entrada para renderizar la interfaz 

row, column: Contenedores para alinear elementos horizontal o verticalmente 

el: Envuelve un elemento simple (por ejemplo, texto o imagen) 

text: Muestra texto

Attribute msg: Propiedad de estilo o comportamiento (como `spacing`, `padding`, `centerX`, `Font.size`, etc.)


Veamos un ejemplo más completo: una pequeña tarjeta de usuario.


import Element exposing (..)

import Element.Background as Background

import Element.Border as Border

import Element.Font as Font


viewUser : Element msg

viewUser =

    el

        [ Background.color (rgb255 240 240 255)

        , Border.rounded 12

        , padding 16

        , width (px 250)

        , centerX

        ]

    <|

        column [ spacing 8 ]

            [ el [ Font.bold, Font.size 20 ] (text "Emanuel Goette")

            , el [ Font.color (rgb255 100 100 100) ] (text "Desarrollador de software")

            , el [] (text "🌎 Argentina")

            ]


Y en el main:


main =

    layout [] viewUser


El resultado: una tarjeta limpia, centrada, con esquinas redondeadas y colores suaves.

Todo expresado como funciones y tipos, sin estilos externos.


Elm UI funciona perfectamente dentro del patrón clásico Model, Update, View.

Ejemplo simple:


import Browser

import Element exposing (..)

import Element.Font as Font


type alias Model =

    { counter : Int }


init : Model

init =

    { counter = 0 }


type Msg

    = Increment

    | Decrement


update msg model =

    case msg of

        Increment ->

            { model | counter = model.counter + 1 }


        Decrement ->

            { model | counter = model.counter - 1 }


view model =

    layout []

        (column [ spacing 16, centerX, centerY ]

            [ el [ Font.size 24 ] (text ("Contador: " ++ String.fromInt model.counter))

            , row [ spacing 10 ]

                [ button "-" Decrement

                , button "+" Increment

                ]

            ]

        )


button label msg =

    el

        [ Background.color (rgb255 100 149 237)

        , Font.color (rgb255 255 255 255)

        , Border.rounded 6

        , paddingXY 16 8

        , mouseDown [ onClick msg ]

        ]

        (text label)


main =

    Browser.sandbox { init = init, update = update, view = view }


Un pequeño contador con estilo nativo y sin HTML.


Elm UI lleva la programación declarativa al diseño visual.

En lugar de pensar en “cómo” maquetar una interfaz, pensás en qué representa cada parte de ella.

“Elm UI te obliga a pensar en tu interfaz como un modelo de datos, no como una plantilla.”

Ideal para quienes buscan precisión, seguridad y coherencia en sus diseños.


Dejo link: 

https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/

https://ellie-app.com



viernes, 31 de octubre de 2025

Lamdera: el poder de Elm en el frontend y el backend


Elm es conocido por su modelo funcional puro, su tipado fuerte y la ausencia de errores en tiempo de ejecución.

Pero históricamente ha tenido una gran limitación: no puede ejecutarse en el backend.

Ahí entra Lamdera, un lenguaje derivado de Elm que propone algo ambicioso:

“Desarrollar aplicaciones fullstack en Elm, con estado compartido entre cliente y servidor, sin escribir ni una línea de JavaScript ni HTTP.”

Lamdera es un lenguaje y un entorno de ejecución creado por James Carlson, completamente compatible con Elm, pero que agrega:

  • Servidor integrado (no hace falta backend separado)
  • Comunicación automática cliente-servidor
  • Persistencia de estado
  • Despliegue con un solo comando


En otras palabras, Lamdera hace que Elm sea fullstack.

El compilador transforma tu código en una aplicación que corre tanto del lado del cliente como del servidor, sin necesidad de gestionar APIs, JSON o sincronización manual.

Lamdera extiende The Elm Architecture (TEA), pero con dos bucles de actualización:

uno en el cliente y otro en el servidor.

Ambos comparten el mismo modelo (Model) y el mismo tipo de mensaje (Msg), por lo que el tipado es consistente en toda la aplicación.

Un proyecto Lamdera se organiza con tres módulos principales:

  • Main.elm
  • Client.elm
  • Server.elm
Main.elm : Define los tipos compartidos entre cliente y servidor:


module Main exposing (..)


import Lamdera exposing (ClientProgram)

import Shared exposing (..)

import Client

import Server


main : ClientProgram Shared.Model Shared.Msg Shared.ToServer Shared.ToClient

main =

    Lamdera.client

        { init = Client.init

        , update = Client.update

        , view = Client.view

        , subscriptions = Client.subscriptions

        , onServerMsg = Client.onServerMsg

        , onClientConnect = Client.onClientConnect

        , onClientDisconnect = Client.onClientDisconnect

        }


Contiene los tipos comunes:


module Shared exposing (..)


type alias Model =

    { counter : Int }


type Msg

    = Increment

    | Decrement


type ToServer

    = SaveCounter Int


type ToClient

    = CounterUpdated Int


Client.elm : Maneja la vista y la interacción del usuario:


module Client exposing (..)


import Lamdera exposing (sendToServer)

import Element exposing (..)

import Shared exposing (..)


init : (Model, Cmd Msg)

init =

    ({ counter = 0 }, Cmd.none)


update : Msg -> Model -> (Model, Cmd Msg)

update msg model =

    case msg of

        Increment ->

            let

                new = model.counter + 1

            in

            ( { model | counter = new }

            , sendToServer (SaveCounter new)

            )


        Decrement ->

            let

                new = model.counter - 1

            in

            ( { model | counter = new }

            , sendToServer (SaveCounter new)

            )


Server.elm: Contiene la lógica del servidor (persistencia, validación, etc.):


module Server exposing (..)


import Lamdera exposing (sendToClient)

import Shared exposing (..)


update : ToServer -> Model -> (Model, Cmd Msg)

update msg model =

    case msg of

        SaveCounter value ->

            ( { model | counter = value }

            , sendToClient (CounterUpdated value)

            )


Con esto ya tenés una aplicación fullstack con comunicación cliente-servidor y estado sincronizado, todo en Elm.


Lamdera incluye herramientas propias:

  • lamdera run → ejecuta el servidor local y abre el navegador
  • lamdera check → analiza tipos y compilación
  • lamdera deploy → compila, sube y publica tu app
  • Dashboard online → permite gestionar el estado y logs


Lamdera todavía no es software libre (su runtime es cerrado, aunque gratuito para uso educativo y hobby).

Tampoco permite acceso directo a archivos o bases de datos externas (aunque se planea extender esto).

Aun así, su estabilidad es sorprendente: muchas aplicaciones en producción usan Lamdera desde hace años sin un solo error en runtime.

Lamdera lleva la promesa de Elm un paso más allá:

> “Sin errores, sin JavaScript, y ahora… sin backend.”


Con él, podés crear aplicaciones reactivas, tipadas y persistentes con una única base de código funcional pura.

Ideal para quienes aman Elm y quieren llevar sus principios hasta el servidor.


Dejo link: https://lamdera.com

https://lamdera.com/doc


martes, 28 de octubre de 2025

Unit Testing en Elm: confianza tipada desde el compilador hasta las pruebas


Elm ya es famoso por su compilador que evita errores en tiempo de ejecución, pero eso no significa que las pruebas no sean necesarias.

De hecho, los tests en Elm complementan su sistema de tipos, ayudándote a verificar la lógica de negocio, las transformaciones de datos y el comportamiento de tus funciones puras.

A diferencia de otros lenguajes donde los tests buscan prevenir errores de nulls, tipos o efectos secundarios, en Elm los tests sirven principalmente para:

  • Asegurar que una función devuelva el resultado esperado.
  • Comprobar que una actualización de modelo (en TEA) cambie el estado correctamente.
  • Validar transformaciones de datos o funciones puras de negocio.


Gracias a la pureza funcional, las pruebas en Elm son simples y predecibles.

El framework oficial de testing es elm-test.


Instalalo con:


npm install -g elm-test

elm-test init


Esto crea una carpeta:


tests/

 └── Example.elm


donde podrás escribir tus pruebas.


Supongamos que tenés una función en src/MathUtils.elm:


module MathUtils exposing (add)


add : Int -> Int -> Int

add a b =

    a + b


Podés crear un test en tests/MathUtilsTest.elm:


module MathUtilsTest exposing (tests)


import Expect

import Test exposing (..)

import MathUtils exposing (add)


tests : Test

tests =

    describe "Pruebas de MathUtils"

        [ test "Suma básica" <|

            \_ -> Expect.equal 4 (add 2 2)

        , test "Suma con negativos" <|

            \_ -> Expect.equal 0 (add 2 -2)

        ]


Ejecutalo con:

elm-test


y deberías ver algo como:


TEST RUN PASSED

Duration: 15 ms


Podés probar también las funciones update de tu arquitectura Elm.

Supongamos una aplicación simple que incrementa un contador:


type alias Model =

    { counter : Int }


type Msg

    = Increment


update : Msg -> Model -> Model

update msg model =

    case msg of

        Increment ->

            { model | counter = model.counter + 1 }


El test sería:


module CounterTest exposing (tests)


import Expect

import Test exposing (..)

import Main exposing (update, Model(..), Msg(..))


tests : Test

tests =

    describe "Update del contador"

        [ test "Incrementa el contador" <|

            \_ ->

                let

                    model = { counter = 0 }

                    updated = update Increment model

                in

                Expect.equal 1 updated.counter

        ]



Expect.notEqual – asegura que dos valores sean diferentes.

Expect.true / Expect.false – comprueba booleanos.

Expect.all – combina varios asserts sobre el mismo valor.

Fuzz tests – pruebas con datos aleatorios (muy útiles para funciones matemáticas o validaciones).


Ejemplo de fuzzing:


import Fuzz exposing (int)


fuzzTest : Test

fuzzTest =

    fuzz int "Suma con cero no cambia el número" <|

        \n -> Expect.equal n (add n 0)


El fuzzer genera cientos de casos aleatorios para asegurar que la propiedad siempre se cumple.

El sistema de tipos de Elm ya evita gran parte de los errores comunes, pero las pruebas unitarias llevan esa confianza un paso más allá. Te permiten documentar y verificar el comportamiento de tus funciones de forma declarativa, legible y segura.

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.


martes, 14 de octubre de 2025

Set en Elm: conjuntos funcionales, simples y seguros


En Elm, un Set representa un conjunto de valores únicos y ordenados, sin repeticiones y con operaciones típicas de teoría de conjuntos: unión, intersección, diferencia, etc.


Un Set se define como:


Set comparable


comparable: el tipo de los valores que puede contener (por ejemplo, Int, String, Char, etc.).


Todos los valores deben ser comparables, es decir, Elm debe poder ordenarlos.

Esto significa que no podés usar listas, records o funciones dentro de un Set.

Podés crearlo vacío o a partir de una lista:


import Set exposing (Set)


-- Conjunto vacío

numeros : Set Int

numeros = Set.empty


-- Desde una lista

pares : Set Int

pares = Set.fromList [2, 4, 6, 8, 10]


Los duplicados se eliminan automáticamente:


Set.fromList [1, 2, 2, 3]

-- Resultado: {1, 2, 3}


Set.member 4 pares

-- True


Set.member 5 pares

-- False


-- Agregar un elemento

paresActualizados =

    Set.insert 12 pares


-- Eliminar un elemento

paresFiltrados =

    Set.remove 8 paresActualizados


Recordá: todo es inmutable. Cada operación devuelve un nuevo Set, sin modificar el original.

Podés convertir un Set a lista:


Set.toList pares

-- [2,4,6,8,10]


Y viceversa:


Set.fromList [3,1,2]

-- {1,2,3}


Para aplicar transformaciones, primero lo convertís a lista y luego de vuelta:


paresDoblados =

    Set.fromList (List.map (\x -> x * 2) (Set.toList pares))


Elm ofrece las funciones clásicas de teoría de conjuntos:


Set.union: Unión de dos conjuntos

Set.union (Set.fromList [1,2]) (Set.fromList [2,3])  --> {1,2,3}


Set.intersect: Intersección 

Set.intersect (Set.fromList [1,2,3]) (Set.fromList [2,3,4])  --> {2,3}


Set.diff: Diferencia

Set.diff (Set.fromList [1,2,3]) (Set.fromList [2])  --> {1,3}


Set.isEmpty:¿Está vacío?

Set.isEmpty Set.empty --> True


Set.size: Cantidad de elementos

Set.size (Set.fromList [1,2,3]) --> 3



Gracias al sistema de tipos de Elm:

  • No hay null ni valores inesperados.
  • No podés mezclar tipos diferentes dentro del mismo Set.
  • Todas las operaciones son puras e inmutables.


Por ejemplo:


-- Esto da error de compilación

Set.fromList [1, "dos", 3]




miércoles, 8 de octubre de 2025

Array en Elm: eficiencia y acceso rápido a los datos


En Elm, las listas (List) son muy comunes, pero cuando necesitamos acceso rápido por índice, actualizaciones eficientes o manejar colecciones grandes, entra en juego Array.

Un Array en Elm es una estructura inmutable y eficiente que permite:

  • Acceder a elementos por índice (O(log n)).
  • Actualizar posiciones específicas sin mutar el original.
  • Convertirse fácilmente a listas (`List`) y viceversa.


Su módulo se importa así:


import Array exposing (Array)


Podés crearlo vacío o a partir de una lista:


-- Array vacío

vacio : Array Int

vacio = Array.empty


-- Desde una lista

numeros : Array Int

numeros = Array.fromList [10, 20, 30, 40]


Y también convertirlo nuevamente a lista:


Array.toList numeros

-- [10, 20, 30, 40]


Para obtener el valor en un índice determinado:


Array.get 2 numeros

-- Just 30


Observá que devuelve un Maybe, ya que el índice podría no existir.

Array.get 10 numeros

-- Nothing


-- Actualizar el valor en la posición 1

actualizado =

    Array.set 1 25 numeros


Array.toList actualizado

-- [10, 25, 30, 40]


Cada modificación devuelve un nuevo Array, sin alterar el original (inmutabilidad funcional).

Para agregar al final, usamos Array.push:


conNuevo =

    Array.push 50 numeros


Array.toList conNuevo

-- [10, 20, 30, 40, 50]


Y para quitar el último, usamos Array.pop:


sinUltimo =

    Array.pop conNuevo


Array.toList sinUltimo

-- [10, 20, 30, 40]


Podés recorrerlo de manera muy parecida a una lista:


Array.map (\x -> x * 2) numeros

-- {20, 40, 60, 80}


Y también filtrarlo o transformarlo:


Array.filter (\x -> x > 20) numeros

-- {30, 40}

Y hay más!! :


Array.length: Devuelve el tamaño          

Array.length numeros --> 4

                       |

Array.isEmpty: ¿Está vacío? 

Array.isEmpty Array.empty --> True


Array.initialize: Crea un array con una función

Array.initialize 5 (\i -> i * 2) --> {0,2,4,6,8}


Array.foldl/Array.foldr: Reduce el array

Array.foldl (+) 0 numeros --> 100                |


Veamos un ejemplo completo:


import Array exposing (Array)


main =

    let

        nums = Array.fromList [1,2,3,4]

        dobles = Array.map (\n -> n * 2) nums

        filtrados = Array.filter (\n -> n > 4) dobles

    in

    Array.toList filtrados

    

-- Resultado: [6,8]


Array en Elm te da:

  • Acceso rápido por índice
  • Inmutabilidad garantizada
  • Operaciones eficientes
  • Interoperabilidad con List


Usá List cuando pienses en secuencias y Array cuando necesites posiciones.


Dict en Elm: Diccionarios funcionales para datos ordenados


En Elm, un Dict (abreviatura de Dictionary) es una estructura de datos inmutable que almacena pares clave–valor, de forma ordenada y segura por tipos.

Un Dict se define como:


Dict comparable value


  • comparable → tipo de la clave (por ejemplo, `String`, `Int`, etc.)
  • value → tipo del valor asociado


Solo podés usar tipos comparables como claves, es decir, tipos con un orden definido (Int, String, Char, etc.).

No podés usar listas, records o funciones como claves.

Podés crear un diccionario vacío o con elementos iniciales:


import Dict exposing (Dict)


-- Vacío

usuarios : Dict Int String

usuarios = Dict.empty


-- Con valores

usuariosIniciales : Dict Int String

usuariosIniciales =

    Dict.fromList

        [ (1, "Ana")

        , (2, "Luis")

        , (3, "María")

        ]


Para buscar un valor por su clave:


Dict.get 2 usuariosIniciales

-- Resultado: Just "Luis"


Dict.get 5 usuariosIniciales

-- Resultado: Nothing


El resultado es un Maybe, lo que evita errores por claves inexistentes.

Podés manejarlo así:


case Dict.get 5 usuariosIniciales of

    Just nombre ->

        "Usuario encontrado: " ++ nombre

    Nothing ->

        "No existe ese usuario."


Insertar y eliminar elementos:


-- Agregar o actualizar

usuarios2 =

    Dict.insert 4 "Sofía" usuariosIniciales


-- Eliminar

usuarios3 =

    Dict.remove 2 usuarios2


Todo es inmutable: estas operaciones devuelven un nuevo Dict, no modifican el original.

Podés convertirlo a lista y trabajar con sus elementos:


Dict.toList usuariosIniciales

-- [(1, "Ana"), (2, "Luis"), (3, "María")]


List.map (\(id, nombre) -> nombre ++ " (" ++ String.fromInt id ++ ")")

    (Dict.toList usuariosIniciales)


O usar funciones específicas:


Dict.map (\_ nombre -> String.toUpper nombre) usuariosIniciales


Dict.member key dict: ¿Existe la clave?                   

Dict.size dict: Cantidad de elementos               

Dict.keys dict: Lista de claves                     

Dict.values dict: Lista de valores                    

Dict.filter pred dict: Filtra según condición              

Dict.union d1 d2: Une dos diccionarios                

Dict.merge: Mezcla con control sobre conflictos 

Dict en Elm es:

  • Inmutable
  • Ordenado por clave
  • Seguro (usa `Maybe` para búsquedas)
  • Funcional y expresivo


En Elm, un Dict es más que un mapa: es una garantía de orden, seguridad y pureza funcional.


lunes, 6 de octubre de 2025

Mónadas en Elm: Encadenando Cálculos con Elegancia


Las mónadas son un concepto central en la programación funcional. Aunque suenen complicadas, en Elm ya las usamos todo el tiempo sin darnos cuenta.

En términos simples: Una mónada es un funtor con una forma de encadenar operaciones que devuelven estructuras.

En Elm esto se hace con funciones como andThen (también llamada bind en otros lenguajes).

Maybe como mónada: Cuando trabajamos con valores opcionales (Maybe), podemos encadenar operaciones sin preocuparnos por el caso Nothing.


dividir : Int -> Int -> Maybe Int

dividir a b =

    if b == 0 then

        Nothing

    else

        Just (a // b)


calculo : Maybe Int

calculo =

    Just 100

        |> Maybe.andThen (\x -> dividir x 2)

        |> Maybe.andThen (\y -> dividir y 5)


-- Resultado: Just 10


Si en algún paso se produce un Nothing, toda la cadena devuelve Nothing automáticamente.


Result como mónada


Con Result, podemos propagar errores sin necesidad de escribir mucho código repetitivo:


parseEntero : String -> Result String Int

parseEntero s =

    case String.toInt s of

        Just n -> Ok n

        Nothing -> Err "No es un número"


invertir : Int -> Result String Float

invertir n =

    if n == 0 then

        Err "División por cero"

    else

        Ok (1 / toFloat n)


calculo : Result String Float

calculo =

    parseEntero "10"

        |> Result.andThen invertir


-- Resultado: Ok 0.1


Si el parseo falla, se corta la cadena con Err. Si no, se sigue con el siguiente cálculo.


List como mónada


Con listas, una mónada nos permite generar todas las combinaciones posibles de elementos:


pares : List (Int, Int)

pares =

    [1, 2]

        |> List.concatMap (\x ->

            [3, 4] |> List.map (\y -> (x, y))

        )


-- Resultado: [(1,3),(1,4),(2,3),(2,4)]


Esto es equivalente a las list comprehensions en Haskell.


Resumen de funciones clave en Elm

  • Maybe.andThen → encadena operaciones opcionales.
  • Result.andThen → encadena operaciones que pueden fallar con error.
  • List.concatMap → encadena operaciones que generan más listas.


Todas siguen el mismo patrón monádico.

En Elm, aunque no hablemos directamente de “Mónadas” en la sintaxis, las usamos constantemente.

domingo, 5 de octubre de 2025

fold en Elm: Reducción de Listas Paso a Paso


En programación funcional, fold (también conocido como reducción) es una técnica para recorrer una lista y acumular un resultado.

En Elm tenemos dos variantes principales:

List.foldl → recorre la lista de izquierda a derecha.

List.foldr → recorre la lista de derecha a izquierda.


Ambos reciben:

  1. Una función acumuladora.
  2. Un valor inicial.
  3. La lista a recorrer.

Y devuelven un único valor.


Veamos un ejemplo:


suma : List Int -> Int

suma lista =

    List.foldl (\x acc -> x + acc) 0 lista

-- suma [1,2,3,4] == 10


Aquí:

  • (\x acc -> x + acc) es la función acumuladora.
  • 0 es el valor inicial.
  • lista es la lista que recorremos.


Otro ejemplo:


concatenar : List String -> String

concatenar palabras =

    List.foldl (\palabra acc -> palabra ++ " " ++ acc) "" palabras


-- concatenar ["Hola", "mundo"] == "Hola mundo "

Ojo: el orden importa. Si usás `foldr`, el resultado cambia:


List.foldr (\palabra acc -> palabra ++ " " ++ acc) "" ["Hola", "mundo"]

-- "Hola mundo "


En este caso no hay diferencia visible, pero con listas grandes o con operaciones no conmutativas sí la hay.


Otro ejemplo: Calcular el producto (foldl vs foldr)


producto : List Int -> Int

producto =

    List.foldl (\x acc -> x * acc) 1

-- producto [1,2,3,4] == 24


Con foldr se obtiene el mismo resultado porque la multiplicación es conmutativa.


Veamos el orden de evaluación:

foldl (izquierda a derecha):


  foldl f acc [1,2,3]

  == f 3 (f 2 (f 1 acc))


foldr (derecha a izquierda):


  foldr f acc [1,2,3]

  == f 1 (f 2 (f 3 acc))


Esto puede cambiar el resultado cuando la operación no es conmutativa, o puede impactar en la performance con listas grandes.


Veamos otro ejemplo: 


invertir : List a -> List a

invertir lista =

    List.foldl (\x acc -> x :: acc) [] lista


-- invertir [1,2,3] == [3,2,1]


En Elm, fold es una herramienta poderosa para:

  • Reducir listas a un único valor.
  • Evitar bucles explícitos.
  • Expresar cálculos de manera declarativa.


Usá foldl cuando quieras acumular de izquierda a derecha (más común y eficiente).

Usá foldr cuando necesites preservar el orden de construcción en estructuras recursivas.


miércoles, 1 de octubre de 2025

Functores en Elm


En programación funcional, un funtor es una estructura de datos que sabe cómo aplicar una función a los valores que contiene, sin que tengamos que preocuparnos por los detalles internos.

En Elm, el concepto de funtor aparece principalmente con el uso de map en distintos tipos.


 ¿Qué es un funtor?

En términos simples:

Un funtor es algo sobre lo que podemos hacer map.

Nos permite aplicar una función a un valor dentro de un contexto (List, Maybe, Result, etc.).

Veamos unos ejemplos: 


Maybe como List:


dobles : List Int

dobles =

    List.map (\x -> x * 2) [1, 2, 3]

-- Resultado: [2, 4, 6]


Acá List es un funtor, porque podemos aplicar una función a todos sus elementos usando map.


Maybe como funtor:


incrementar : Maybe Int -> Maybe Int

incrementar valor =

    Maybe.map (\x -> x + 1) valor


-- incrementar (Just 5) == Just 6

-- incrementar Nothing == Nothing


El map de Maybe aplica la función solo si hay un valor (Just), y deja todo igual si es Nothing.


Result como funtor


toUppercase : Result String String -> Result String String

toUppercase resultado =

    Result.map String.toUpper resultado


-- toUppercase (Ok "elm") == Ok "ELM"

-- toUppercase (Err "Error") == Err "Error"


El map de Result aplica la función al valor exitoso (Ok), y en caso de error (Err) no hace nada.


Todo funtor debe cumplir dos leyes:

1. Identidad:

   List.map identity xs == xs

   Aplicar la función identity no debe cambiar nada.


2. Composición:

   List.map (f >> g) xs == (List.map f >> List.map g) xs

   Aplicar dos funciones compuestas debe ser lo mismo que aplicarlas de a una.


En Elm, los funtores nos permiten:

  • Aplicar funciones de forma elegante a valores dentro de contextos.
  • Evitar escribir lógica repetida para casos como Nothing o Err.
  • Pensar en términos más declarativos y expresivos.

Siempre que uses map en List, Maybe o Result, ¡estás usando funtores en Elm!

viernes, 26 de septiembre de 2025

Simulando Listas por Comprensión en Elm


En lenguajes como Haskell o Python, las listas por comprensión permiten generar y transformar listas con una sintaxis muy compacta.

En Elm no existen listas por comprensión como sintaxis, pero sí podemos expresar lo mismo con funciones de orden superior como map, filter y concatMap.

En Haskell:


[x * 2 | x <- [1..5]]

-- Resultado: [2,4,6,8,10]


En Elm:


dobles : List Int

dobles =

    [1,2,3,4,5]

        |> List.map (\x -> x * 2)


Resultado: [2,4,6,8,10]


Con condición (filter)


En Haskell:


[x * 2 | x <- [1..5], x > 2]

-- Resultado: [6,8,10]


En Elm:


doblesMayoresQueDos : List Int

doblesMayoresQueDos =

    [1,2,3,4,5]

        |> List.filter (\x -> x > 2)

        |> List.map (\x -> x * 2)


-- Resultado: [6,8,10]


Generando pares (concatMap)


En Haskell:


[(x,y) | x <- [1,2], y <- [3,4]]

-- Resultado: [(1,3),(1,4),(2,3),(2,4)]


En Elm:


pares : List (Int, Int)

pares =

    [1,2]

        |> List.concatMap (\x ->

            [3,4] |> List.map (\y -> (x, y))

        )


-- Resultado: [(1,3),(1,4),(2,3),(2,4)]


Aunque Elm no tiene listas por comprensión como sintaxis, podemos lograr la misma expresividad combinando funciones de orden superior:

  • List.map → transformación.
  • List.filter → condiciones.
  • List.concatMap → anidación (equivalente a los múltiples generadores de Haskell).

De esta forma, Elm mantiene un estilo declarativo y expresivo, alineado con su filosofía de simplicidad y claridad.

Lazy Evaluation en Elm: ¿Existe?


En muchos lenguajes funcionales, como Haskell, la lazy evaluation (evaluación perezosa) es una característica central: los valores no se calculan hasta que realmente se necesitan.

En Elm, en cambio, la evaluación es estricta por defecto (eager evaluation), lo que significa que todos los argumentos de una función se evalúan inmediatamente antes de ser usados.


Veamos un ejemplo simple:


calcular : Int -> Int -> Int

calcular x y =

    x + 1


resultado =

    calcular 5 (Debug.log "evaluando..." 10)


Al ejecutar este código, veremos en la consola:


evaluando...


Aunque el parámetro y nunca se use, Elm lo evalúa igual, porque siempre evalúa sus argumentos antes de ejecutar la función.


Aunque Elm no es lazy por diseño, sí ofrece el módulo Lazy, que permite diferir la evaluación de expresiones costosas hasta que realmente las necesitemos.


import Lazy exposing (Lazy)


perezoso : Lazy Int

perezoso =

    Lazy.lazy (\_ -> 1 + 2)


resultado : Int

resultado =

    Lazy.force perezoso


En este caso:

  • Lazy.lazy encapsula un cálculo.
  • Lazy.force ejecuta el cálculo cuando es necesario.


¿Cuándo usar Lazy?


El módulo Lazy es útil en:

  • Estructuras grandes que no siempre se recorren por completo.
  • Cálculos costosos que solo se necesitan en ciertas condiciones.
  • Simulación de comportamiento perezoso, para optimizar rendimiento.


Sin embargo, Elm sigue favoreciendo la evaluación estricta en la mayoría de los casos, para mantener la simplicidad y la predictibilidad del lenguaje.

Pattern Matching en Elm: Desestructurando Datos de Forma Segura


El pattern matching en Elm es una herramienta poderosa para trabajar con listas, tuplas, registros y tipos algebraicos.

Permite desestructurar datos y cubrir todos los casos posibles de manera clara y segura, gracias al compilador.

Pattern Matching con listas:


primerElemento : List Int -> String

primerElemento lista =

    case lista of

        [] ->

            "Lista vacía"

        x :: xs ->

            "El primer elemento es " ++ String.fromInt x


[] → lista vacía.

x :: xs → descompone la lista en el primer elemento x y el resto xs.


Ejemplo:


primerElemento []          -- "Lista vacía"

primerElemento [10,20,30]  -- "El primer elemento es 10"


Pattern Matching con tuplas


sumaTupla : (Int, Int) -> Int

sumaTupla tupla =

    case tupla of

        (a, b) ->

            a + b


sumaTupla (3, 4)   -- 7


También podés combinar:


describeTupla : (String, Int) -> String

describeTupla (nombre, edad) =

    nombre ++ " tiene " ++ String.fromInt edad ++ " años"


Pattern Matching con registros


type alias Persona =

    { nombre : String

    , edad : Int

    }


saludo : Persona -> String

saludo persona =

    case persona of

        { nombre, edad } ->

            "Hola " ++ nombre ++ ", tenés " ++ String.fromInt edad ++ " años"


Ejemplo:

saludo { nombre = "Ana", edad = 30 }

-- "Hola Ana, tenés 30 años"


Pattern Matching con tipos algebraicos


Elm brilla con sus tipos definidos por el usuario.


type Resultado

    = Exito String

    | Error String


procesar : Resultado -> String

procesar res =

    case res of

        Exito mensaje ->

            "Todo salió bien: " ++ mensaje

        Error mensaje ->

            "Ocurrió un error: " ++ mensaje


Ejemplo:


procesar (Exito "Archivo guardado")

-- "Todo salió bien: Archivo guardado"


procesar (Error "Permiso denegado")

-- "Ocurrió un error: Permiso denegado"


Algo clave en Elm: el compilador exige cubrir todos los casos posibles en un case.

Si olvidás uno, el código no compila → esto evita errores en tiempo de ejecución.


  • case ... of permite desestructurar listas, tuplas, registros y tipos algebraicos.
  • Es una forma segura y declarativa de trabajar con datos.
  • El compilador obliga a cubrir todos los casos, garantizando que no haya estados no contemplados.


 El pattern matching es uno de los pilares que hace a Elm un lenguaje expresivo y confiable.


sábado, 20 de septiembre de 2025

Registros en Elm


En Elm, un registro es una colección de pares clave = valor, similar a un objeto en otros lenguajes, pero inmutable y tipado.

Son muy usados para agrupar datos de manera clara y segura.


persona : { nombre : String, edad : Int }

persona =

    { nombre = "Ana", edad = 30 }


  • nombre es de tipo String.
  • edad es de tipo Int.


nombreDeAna : String

nombreDeAna =

    persona.nombre

-- Resultado: "Ana"


También se puede usar una función anónima para acceder:


List.map .nombre [ persona, { nombre = "Luis", edad = 25 } ]

-- ["Ana", "Luis"]


Los registros en Elm son inmutables.

Para "cambiar" un campo, se crea una copia con el campo modificado:


personaMayor : { nombre : String, edad : Int }

personaMayor =

    { persona | edad = 31 }


Esto no altera persona, sino que genera un nuevo registro.

Las funciones pueden recibir registros directamente:


saludar : { nombre : String } -> String

saludar r =

    "Hola, " ++ r.nombre

saludar persona

-- "Hola, Ana"


Cuando un registro es usado en varios lugares, conviene definir un alias de tipo:


type alias Persona =

    { nombre : String

    , edad : Int

    }


juan : Persona

juan =

    { nombre = "Juan", edad = 40 }


cumplirAnios : Persona -> Persona

cumplirAnios p =

    { p | edad = p.edad + 1 }


Podemos "abrir" un registro en sus campos:


mostrar : Persona -> String

mostrar { nombre, edad } =

    nombre ++ " tiene " ++ String.fromInt edad ++ " años"

mostrar juan

-- "Juan tiene 40 años"


En Resumen:

  • Un registro agrupa datos con nombres de campo.
  • Son inmutables, para "modificarlos" se usa la sintaxis { r | campo = nuevoValor }.
  • Los alias de tipo (type alias) hacen el código más claro.
  • Se puede usar desestructuración para acceder fácilmente a los campos.


En Elm, los registros reemplazan el concepto de objetos de otros lenguajes, pero sin herencia ni mutabilidad.

martes, 16 de septiembre de 2025

Tipos Genéricos en Elm


En Elm, al igual que en muchos lenguajes funcionales, los tipos genéricos permiten escribir funciones y estructuras de datos que funcionan con diferentes tipos sin necesidad de duplicar código.

Un tipo genérico se indica con letras minúsculas como a, b, c, etc.

Veamos un ejemplo simple:


identity : a -> a

identity x =

    x


Aquí, la función identity recibe un valor de cualquier tipo a y lo devuelve.

  • Si le paso un Int, devuelve un Int.
  • Si le paso un String, devuelve un String.


identity 42

-- Resultado: 42

identity "hola"

-- Resultado: "hola"


Las listas en Elm también son genéricas.

Su tipo es List a, donde a puede ser cualquier tipo.


longitud : List a -> Int

longitud lista =

    List.length lista


longitud puede recibir:

longitud [1, 2, 3]          -- funciona con List Int

longitud ["a", "b", "c"]    -- funciona con List String


En ambos casos la función devuelve la cantidad de elementos, sin importar de qué tipo sean.


Las tuplas también soportan tipos genéricos:


swap : (a, b) -> (b, a)

swap (x, y) =

    (y, x)


Ejemplo de uso:


swap (1, "uno")

-- Resultado: ("uno", 1)


swap (True, 3.14)

-- Resultado: (3.14, True)


Podemos crear estructuras más complejas con varios genéricos:


maybeFirst : List a -> Maybe a

maybeFirst lista =

    case lista of

        [] ->

            Nothing


        x :: _ ->

            Just x


  • Si la lista está vacía, devuelve Nothing.
  • Si tiene elementos, devuelve Just el primero.


Funciona con cualquier tipo de lista:


maybeFirst [1, 2, 3]       -- Just 1

maybeFirst ["a", "b"]      -- Just "a"

maybeFirst []              -- Nothing


Los tipos genéricos se escriben con letras minúsculas (a, b, c).

  • Permiten que funciones y estructuras trabajen con cualquier tipo.
  • Están presentes en funciones (identity), listas (List a), tuplas ((a, b)), y en tipos como Maybe a.
  • Nos ayudan a escribir código más reutilizable y flexible.



domingo, 14 de septiembre de 2025

Lambdas en Elm


En Elm, las lambdas (o funciones anónimas) son una herramienta fundamental para escribir código conciso y expresivo. Una lambda es simplemente una función sin nombre, definida directamente en el lugar donde se necesita.

La sintaxis de una lambda es:

\param1 param2 -> expresion


Por ejemplo:

\x -> x + 1


Es una función que recibe un número x y devuelve x + 1.


Con más de un parámetro:

\a b -> a + b


Las lambdas son muy útiles cuando trabajamos con funciones como map, filter o fold:


List.map (\x -> x * 2) [1,2,3]

-- Resultado: [2,4,6]


List.filter (\x -> x > 5) [3,7,1,8]

-- Resultado: [7,8]


Las lambdas pueden capturar variables de su entorno, convirtiéndose en clausuras:


sumarN : Int -> List Int -> List Int

sumarN n lista =

    List.map (\x -> x + n) lista


sumarN 5 [1,2,3]

-- Resultado: [6,7,8]


Acá la lambda \x -> x + n recuerda el valor de n aunque se ejecute después.

En Elm, los operadores son funciones. Esto significa que:


(+) 3 4 -- 7

(<) 2 5 -- True


Y en lugar de escribir:

\a b -> a < b


Podés usar directamente:

(<)


Ejemplo:

List.sortWith (<) [3,1,2]

-- Resultado: [1,2,3]


Todas las funciones en Elm son curried. Esto significa que se pueden aplicar parcialmente:


(>) 10

-- Es equivalente a \x -> 10 > x


List.filter ((>) 5) [1,7,3,9]

-- Resultado: [1,3]


Las lambdas en Elm hacen que trabajar con funciones de orden superior sea simple y elegante. Entre la posibilidad de capturar variables (clausuras), usar operadores como funciones y aplicar parcialmente, Elm ofrece un estilo de programación muy expresivo y seguro.

jueves, 11 de septiembre de 2025

Listas en Elm: Una Guía Práctica


En Elm, las listas son una de las estructuras de datos más utilizadas. Son colecciones inmutables de elementos del mismo tipo, y ofrecen muchas funciones para trabajar con ellas de forma segura y declarativa.

Podés crear una lista escribiendo los elementos entre corchetes [] separados por comas:


numeros : List Int

numeros = [1, 2, 3, 4, 5]


palabras : List String

palabras = ["hola", "elm", "listas"]


Una lista vacía se define como:


vacia : List Int

vacia = []


Acceder al primer elemento


List.head [1,2,3] -- Just 1


 Acceder al resto de la lista


List.tail [1,2,3] -- Just [2,3]


Largo de la lista


List.length [1,2,3] -- 3


Concatenar listas


[1,2] ++ [3,4] -- [1,2,3,4]


Aplica una función a cada elemento:


List.map (\x -> x * 2) [1,2,3] -- [2,4,6]


Filtra elementos que cumplen una condición:


List.filter (\x -> x > 2) [1,2,3,4] -- [3,4]


Reduce una lista a un único valor:


List.foldl (+) 0 [1,2,3,4] -- 10


Elm ofrece el operador :: para construir listas agregando un elemento al inicio:


1 :: [2,3,4] -- [1,2,3,4]


"hola" :: ["elm", "listas"] -- ["hola","elm","listas"]


Ejemplo completo :


numeros : List Int

numeros = [1, 2, 3, 4, 5]


pares : List Int

pares = List.filter (\n -> modBy 2 n == 0) numeros


cuadrados : List Int

cuadrados = List.map (\n -> n * n) 


main =

    Debug.log "Pares" pares

    |> Debug.log "Cuadrados" cuadrados


Las listas en Elm son poderosas, seguras y fáciles de manipular gracias a la librería estándar. Una vez que domines map, filter y fold, vas a poder expresar la mayoría de las transformaciones sobre colecciones de forma clara y concisa.




domingo, 7 de septiembre de 2025

Comandos y suscripciones

 Anteriormente vimos cómo la arquitectura Elm gestiona las interacciones del ratón y el teclado, pero ¿qué ocurre con la comunicación con los servidores? ¿Con la generación de números aleatorios?

Para responder a estas preguntas, es útil comprender mejor cómo funciona la arquitectura Elm en segundo plano. Esto explicará por qué las cosas funcionan de forma ligeramente diferente a lenguajes como JavaScript, Python, etc.


Sandbox

No le he dado mucha importancia, pero hasta ahora todos nuestros programas se crearon con Browser.sandbox. Proporcionamos un modelo inicial y describimos cómo actualizarlo y visualizarlo.

Puedes imaginar Browser.sandbox como la configuración de un sistema como este:



Nos mantenemos en el mundo de Elm, escribiendo funciones y transformando datos. Esto se conecta al sistema de ejecución de Elm. Este sistema determina cómo renderizar HTML eficientemente. ¿Ha cambiado algo? ¿Cuál es la modificación mínima del DOM necesaria? También detecta cuándo alguien hace clic en un botón o escribe en un campo de texto. Lo convierte en un mensaje y lo introduce en el código de Elm.

Al separar claramente toda la manipulación del DOM, es posible utilizar optimizaciones extremadamente agresivas. Por lo tanto, el sistema de ejecución de Elm es una de las razones principales por las que Elm es una de las opciones más rápidas disponibles.


element

En los siguientes ejemplos, usaremos Browser.element para crear programas. Esto introducirá los conceptos de comandos y suscripciones que nos permiten interactuar con el mundo exterior.

Puedes imaginar Browser.element como la configuración de un sistema como este:


Además de generar valores HTML, nuestros programas también enviarán valores Cmd y Sub al sistema de ejecución. En este contexto, nuestros programas pueden ordenar al sistema de ejecución que realice una solicitud HTTP o genere un número aleatorio. También pueden suscribirse a la hora actual.

Creo que los comandos y las suscripciones cobran más sentido al ver ejemplos, por lo tanto, en los próximos posts veremos ejemplos.