« Ejercicios de Elm (Se… « || Inicio || » Elm: Señales »

Elm: Introducción

Última modificación: 7 de Junio de 2016, y ha tenido 1383 vistas

Etiquetas utilizadas: || ||

 Elm es un lenguaje funcional pensado para ejecutarse en el navegador, por lo que tiene capacidades específicas para crear aplicaciones web que hace uso de técnicas basadas en Programación Funcional Reactiva. Es relativamente nuevo, fuertemente basado en Haskell, y evoluciona rápidamente, por lo que puede haber detalles del mismo que pueden ser cambiados en versiones posteriores. La versión del lenguaje en el momento de escribir estas notas es la 0.16, y se considera ya estable en gran parte del núcleo y de sus librerías principales.

El siguiente video muestra el desarrollo de Elm de una forma animada a partir de las acciones tomadas en Github, que es donde se almacena:

Gource Visualization of Elm

Un primer programa

Vamos a comenzar con el típico ejemplo de “Hola Mundo”, pero con salida en una página web:

import Html exposing (text)
main = text "¡Hola, Mundo!"

La ejecución principal de un programa Elm se realiza por medio de la función main. Todo programa debe definir una función de este tipo, que será quien lleve la carga del flujo principal del programa y, como peculiaridad, no puede tener parámetros (ya que es la función que se ejecuta de forma independiente durante la carga de la aplicación web asociada).

En este caso, el cuerpo de la función contiene una llamada a la función text (de la librería Html, que se importa como funcionalidad adicional al principio del programa), que recibe un argumento, la cadena “¡Hola Mundo!”, que viene delimitada por comillas dobles, y devuelve un dato de tipo Html, que es simplemente un alias para devolver un Nodo del árbol DOM para la página web (si quieres saber más acerca de DOM puedes mirar aquí). La signatura de la función text es la siguiente:

Text : String -> Html

El programa anterior muestra el mensaje “Hola Mundo” usando la fuente por defecto y sin muchas opciones de personalización, pero más adelante veremos algunas opciones de esta librería, y de otras adicionales, que nos darán un control absoluto sobre el qué y cómo manipular y representar la información desde un punto de vista completamente funcional.

Si quieres probarlo puedes usar el editor online disponible en la página oficial del lenguaje para poder hacer pruebas, y al que puedes acceder aquí: http://elm-lang.org:1234/try (versión 0.16)

Diferencias con Haskell

Aunque ambos son lenguajes funcionales y Elm se ha inspirado fuertemente en muchas de las características de Haskell, que es actualmente el representante canónico de este paradigma, hay algunas otras características en las que Elm ha seleccionado su inspiración en otros lenguajes funcionales, como ML. Por ello, y ya que esta serie de entradas está orientada a usuarios con conocimientos de Haskell, debemos destacar las principales diferencias desde el punto de vista sintáctico que vamos a encontrar:

  1. Para definir una función hacemos uso de : (en Haskell se hace uso de ::).
  2. El operador que concatena la cabeza de una lista con la cola es :: (en Haskell se hace uso de :).
  3. Para definir una lista de tipo a se hace uso de la palabra reservada List (es decir, List a, mientras que en Haskell se escribe [a]).
  4. En Elm no existe la estructura where, pero sí se puede hacer uso de let.
  5. Para reducir el uso de paréntesis se puede hacer uso de los operadores |> y <| (en Haskell se hace uso de $, esta notación de Elm está tomada de F#). Veremos ejemplos más adelante, pero su uso es: f <| x == f x == x |> f. Por lo tanto, una expresión con paréntesis como f ( g x) se puede escribir: f <| g <| x .
  6. La composición de funciones se hace con los operadores de orden superior << y >> (en Haskell la forma habitual es f . g).
  7. Elm hace un uso muy elaborado de los registros (puedes ver este documento para una explicación completa de su uso)

Por lo demás, se puede decir que ambos lenguajes son tan similares que un usuario de uno de los dos puede leer y escribir de forma natural en cualquiera de los dos lenguajes. En cualquier caso, debe ser de obligada lectura para poder entrar a trabajar con Elm el documento Elm Syntax que se mantiene en su sitio web.

Librerías Core de Elm

Todo proyecto de Elm necesita las librerías Core, que proporcionan las funcionalidades básicas:

  • Operaciones numéricas básicas.
  • Estructuras de datos básicas, como listas, diccionarios y conjuntos.
  • Una implementación de Señales.
  • Funcionas básicas de representación.

Para ello, automáticamente se realizan las siguientes importaciones:

import Basics exposing (..)
import List exposing ( List, (::) )
import Maybe exposing ( Maybe( Just, Nothing ) )
import Result exposing ( Result( Ok, Err ) )
import Signal exposing ( Signal )

La intención con esta importanción por defecto es incluir todo aquello que es muy usual y que difícilmente se pisará con funcionalidades que nadie más escribirá en una lilbrería.  Se intenta mantener el conjunto de importaciones por defecto lo más pequeño posible. En este sentido, estas cargas iniciales juegan el mismo papel que el Prelude de Haskell.

Es imprescindible echar un vistazo a la librería Basics, para saber qué funciones se consideran básicas directamente en Elm (el resto de funciones que se cargan por defecto de otras librerías se puede ver en la importanción anterior).

Primeros ejemplos

Además, y con el fin de realizar las primeras pruebas, vamos a necesitar una función que viene en una librería adicional: la función show de la librería Graphics.Element, que convierte algunos tipos de datos en elementos que son representables en el navegador (funciona directamente en: Int, Float, Bool, String y tipos compuestos formados a partir de ellos, como son listas y pares). No da muchas opciones adicionales de forma directa, pero puede ser muy útil para hacer comprobaciones directas o debugging. Su signatura es show : a -> Element (más adelante veremos el tipo Element y algunas otras funciones muy útiles que hay en la misma librería para manipularlo, por ahora nos basta con saber que es un tipo de dato que se puede representar directamente en el navegador). Un ejemplo sería:

import Graphics.Element exposing (..)
main : Element
main =
    show "¡Hola, Mundo!"

De hecho, esta función está definida de la siguiente forma:

show value = leftAligned (Text.monospace (Text.fromString (toString value)))
Donde cada función que interviene hace lo siguiente: 
  • toString : a -> String (de la librería Basics), convierte cualquier valor en una cadena.
  • fromString : String -> Text (de la librería Text), convierte una cadena en un texto que puede ser mostrado y estilizado.
  • monospace : Text -> Text (de la librería Text), convierte el texto a tipo monoespacio.
  • leftAligned : Text -> Element (de la librería Graphics.Element), genera un Element con el texto justificado a la izquierda del bloque

    . Otras similares son rightAligned, centered y justified.

Puede ser interesante echar un vistazo a la librería Text para ver de qué forma se pueden modificar los datos de tipo Text con el fin de mejorar la presentación en el navegador. De todas formas, más adelante veremos cómo hacer uso de las librerías Graphics.Element y Html para fines similares. Por ejemplo, haciendo uso de los operadores de reducción de paréntesis, podemos hacer algo como:

import Graphics.Element as GE
import Text
import Color exposing (..)
    
main : GE.Element
main = Text.fromString "¡Hola, Mundo!" |>
    Text.color red |>
    Text.italic |>
    Text.bold |>
    Text.height 60 |>
    GE.leftAligned

En el código anterior hemos hecho uso de dos formas de importar las librerías. Por defecto, cuando se quiere hacer uso de una función de una librería (que ha de ser importada si no es una de las que se importan por defecto), hemos de anteponer el nombre de la librería al nombre de la función (seprándolos con un punto), como por ejemplo en Text.italic. En este caso, podemos cambiar el nombre del prefijo para facilitar la escritura del código (como hemos hecho con la librería Graphics.Element, que la calificamos como GE). Sin embargo, si usamos exposing al importar una librería (como estamos haciendo en la librería Color), estamos indicando que el espacio de nombres de la librería se mezcle con el espacio de nombres del módulo que estamos escribiendo. En este caso, podemos exponer todas las funciones de la librería (haciendo uso de (..)) o poner entre paréntesis las funciones que se quieren exponer abiertamente.

Puede ser interesante ver cómo se escribiría el código anterior sin hacer uso del operador de aplicación, |> :

import Graphics.Element as GE
import Text
import Color exposing (..)
    
main : GE.Element
main = GE.leftAligned (Text.height 60 (Text.bold (Text.italic 
(Text.color red (Text.fromString "Hola, Mundo!")))))

Si hubiésemos querido definir funciones intermedias que facilitasen cambiar las propiedades del texto a mostrar, y haciendo uso del operador de aplicación contrrario, podríamos hacer escrito algo como:

import Graphics.Element as GE
import Text
import Color exposing (..)

main : GE.Element
main = formato 60 blue "Hola, Mundo!"

formato : Float -> Color -> String -> GE.Element
formato tam col texto =
GE.leftAligned <|
Text.height tam <|
Text.bold <|
Text.italic <|
Text.color col <|
Text.fromString texto

Junto a las opciones de formatear directamente el texto, Elm también permite el uso del formato Markdown (puedes saber más acerca de este formato aquí) gracias a la librería Markdown,cque tiene una sola función que recibe una cadena de texto y devuelve un elemento renderizable en el navegador correctamente procesado (puedes ver un ejemplo de su uso aquí).

Colección de ejemplos

Con estos conocimientos en mente, ya podemos ver algunos de los ejemplos introductorios que se proporcionan en la propia página de Elm. En concreto, estamos interesados ahora mismo en analizar los siguientes ejemplos de la sección Core:

En este punto se recomienda leer algunas partes de la documentación que podemos encontrar en la página oficial de Elm, principalmente la relacionada con la parte Core del lenguaje, y con la Sintaxis. Además, puede ser interesante que se mire la Guía de Estilo que recomiendan en el propio sitio.

Posteriormente, será necesario que sepas cómo funciona el sistema de tipos en Elm. Puedes encontrar información sobre ello en la documentación de Elm, bajo el nombre Model the Problem.

Instalación

La mayoría de las veces, para hacer el tipo de pruebas más habituales y trabajar con proyectos pequeños, no es necesario tener instalado Elm y podemos hacer uso de cualquier de los editores online que permiten ejecutar código, pero para proyectos más grandes, o para compilar los programas que vamos haciendo y obtener los ficheros JS que se obtienen (incluso los html asociados que ejecutan directamente el JS obtenido), puede ser interesante tener una instalación en local y usar las características adicionales que proporciona. 

Para instalar Elm y poder ejecutarlo en local ha de tenerse instalado node.jsNode es un intérprete Javascript del lado del servidor que cambia la noción de cómo debería trabajar un servidor. Su meta es permitir a un programador construir aplicaciones altamente escalables y escribir código que maneje decenas de miles de conexiones simultáneas en una sólo una máquina física. Tanto para Node como para Elm existen versiones en Windows, Mac y Linux de instalación sencilla. Además, es aconsejable instalar Elm en una carpeta donde haya permisos de escritura (quizás fuera de la carpeta "Archivo de Programas") para que no tenga problemas de ejecución.

Elm viene con varios ejecutables desde la línea de comandos que proporcionan diferentes servicios (los puedes encontrar en la carpeta \bin de la instalación, y son: elm-repl, elm-make, elm-reactor y elm-package). Como todos ellos necesitan node.js para funcionar, es preferible arrancarlos usando la consola de comandos que trae el propio node.js para estar seguros de que éste estará funcionando en la capa inferior y así asegurar el correcto funcionamiento de los ejecutables de Elm. Veamos brevemente en qué consiste cada uno de ellos:

elm-repl

Un REPL (Read-Eval-Print Loop) es un entorno que permite lanzar comandos sencillos de un lenguaje de programación y obtener resultados de forma interactiva. Lanzado desde una consola de comandos, elm-repl (que necesita de node.js ejecutándose de fondo) tiene el aspecto de un evaluador sencillo, pero que permite lanzar la ejecución de comandos de elm una forma sencilla y directa:

PROMPT> elm-repl
Elm REPL 0.4.2 (Elm Platform 0.15.1)
  See usage examples at <https://github.com/elm-lang/elm-repl>
  Type :help for help, :exit to exit
>

Por ejemplo:

> import String
> import List
> nombre = "Elm"
"Elm" : String
> "Probando " ++ nombre ++ " interactivamente"
"Probando Elm interactivamente" : String
> "Probando " ++ String.toUpper nombre ++ "interactivamente"
"Probando ELM interactivamente" : String
> nombres = ["elm", "js", "node"]
["elm","js","node"] : List String
> List.map String.toUpper nombres
["ELM","JS","NODE"] : List String

Para salirse del intérprete basta escribir el comando :exit. Disponer de un buen REPL es fundamental para hacer comprobaciones rápidas, aunque hay que admitir que no haremos un uso intensivo de él con Elm debido a que estamos interesados en su parte gráfica e interactiva, así como en su uso para manipular elementos en páginas web.

elm-make

A medida que los proyectos con Elm crezcan y hagamos uso de un mayor número de módulos para componer la aplicación completa, el REPL se queda corto y necesitaremos herramientas que nos permitan compilar el producto final a partir de todas las partes que los componen. Esa es la función que cumple elm-make. La forma de trabajar con él es la siguiente:

  1. Crear una carpeta para el proyecto que vamos a generar y meter dentro todos los ficheros que intervengan. Por convención, supondremos que hay un fichero llamado Main.elm que contiene la función main.
  2. Para compilar el fichero Main.elm, y obtener un fichero resultado.html basta escribir:
    elm-make Main.elm --output resultado.html
    Si el proyecto usa librerías u otros módulos, elm-make se encarga de añadir todo lo necesario para su ejecución.
  3. Abrir el fichero resultado.html en el navegador.

elm-reactor

Como el proceso anterior puede ser un poco pesado cuando estamos en el ciclo de creación y depuración, Elm viene con la herramienta elm-reactor que proporciona un pequeño servidor web que nos permite ejecutar en local directamente los archivos elm sin necesidad de compilarlos (la compilación la hace al vuelo). De esta forma, basta cargar el fichero main.elm desde elm-reactor (el servidor nos permite usar el navegador web para navegar por el directorio de proyectos, y seleccionar main.elm directamente haciendo clic sobre él), modificar los archivos con nuestro editor de texto favorito, y recargar la página para ver automáticamente los cambios realizados (tal y como hace el editor online que se proporciona desde la página de elm).

Para acceder al servidor hay que ejecutar elm-reactor (recuerda, siempre desde un entorno node.js) y posteriormente abrir http://localhost:8000. Mostrará como carpeta raíz aquella desde donde se haya ejecutado elm-reactor.

Una característica impresionante de elm-reactor es que admite lo que se llama hot-swapping, lo que significa que se pueden recargar los cambios sin necesidad de reiniciar toda la aplicación. De esta forma, hace uso de una de las características más llamativas de la programación funcional y que sería imposible en un lenguaje imperativo. Además, permite acceder también al Time Traveling Debugger, que permite replicar las ejecuciones completas de la aplicación y ver cómo ha evolucionado el sistema (e incluso crear desde cualquier punto una variante en la ejecución).

elm-package

Por último, elm-package permite instalar de forma sencilla librerías de Elm Package Catalog, para ello basta ejecutar:

elm-package install Nombre-completo-paquete

Recuerda que el nombre completo del paquete debe incluir el nombre de la carpeta en la que está incluído, por ejemplo:

elm-package install evancz/automaton

« Ejercicios de Elm (Se… « || Inicio || » Elm: Señales »