Elm (llenguatge de programació)

De la Viquipèdia, l'enciclopèdia lliure
Infotaula de llenguatge de programacióElm
Tipusfunctional reactive programming language (en) Tradueix, llenguatge de programació funcional, reactive programming language (en) Tradueix, llenguatge de programació, llenguatge de programació purament funcional i llenguatge de regles fora de joc Modifica el valor a Wikidata
Data de creació2012 Modifica el valor a Wikidata
DissenyEvan Czaplicki (en) Tradueix Modifica el valor a Wikidata
Paradigma de programacióprogramació funcional, programació reactiva, purely functional programming (en) Tradueix i functional reactive programming (en) Tradueix Modifica el valor a Wikidata
Darrera versió estable0.19.1 () Modifica el valor a Wikidata
Llenguatge de programacióHaskell Modifica el valor a Wikidata
Influenciat perHaskell, ML Estàndard, OCaml i F Sostingut Modifica el valor a Wikidata
Extensió dels fitxerselm Modifica el valor a Wikidata
Codi fontCodi font Modifica el valor a Wikidata
Llicènciallicència BSD Modifica el valor a Wikidata
Etiqueta d'Stack ExchangeEtiqueta Modifica el valor a Wikidata
Pàgina webelm-lang.org Modifica el valor a Wikidata

Elm és un llenguatge de programació funcional i tipatge fort per crear interfícies d'usuari basades en navegadors web, generant estructures dinàmiques basades en JavaScript.

Elm utilitza la programació reactiva funcional i un sistema de gràfics funcional pur per construir interfícies d'usuari sense actualitzacions destructives.

Utilitza una sintaxi molt similar al Haskell però amb diferent semàntica. Elm és d'avaluació estricta (l'avaluació va de l'arrel a les branques començant per la funció main) i primerenca (en un àmbit local les instruccions s'executen per ordre d'aparició; no hi ha clàusules where).

Descripció[modifica]

La implementació d'Elm compila a JavaScript i HTML, amb ús d'estils CSS.

A Elm, la programació reactiva funcional, basada en objectes variables i relacions de dependència, relleva l'usuari de programar gestors d'esdeveniments (ang: event handlers) i rutines en mode d'inversió de control (ang:callbacks). També actualitza automàticament els objectes en pantalla.

La presentació gràfica funcional pura relleva d'actualitzar el model de document DOM.

Elm també permet incloure plantilles en llenguatge de marques Markdown.

Sintaxi i semàntica[modifica]

Elm adopta una sintaxi a l'estil del Haskell amb influències d'OCaml i F Sostingut. Per exemple l'operador "té tipus" s'escriu amb un únic caràcter ':'; els operadors d'aplicació directa (<|) i aplicació revessa (|>) són tals que (f x) és igual que (f |> x) i igual que (x |> f).[1]

Les definicions de tipus segueixen l'esquema del Haskell. Els tipus dels paràmetres se separen amb (->) i la repetició de variables de tipus indica requeriment de coincidència.

Elm té registres extensibles que forneixen un ús segur de la flexibilitat del model d'objectes de Javascript.

El sistema de tipus suporta tipus primitius com Enters i Coma flotant, dades estructurades com ara tuples i registres, i també tipus abstractes de dades personalitzats.

La implementació de la programació reactiva funcional és moguda per esdeveniments (event-driven), volent dir que les actualitzacions són automàtiques i només en cas de necessitat. Comparteix la màxima semblança amb l'"Event-driven FRP"[2][3] i l'"Arrowized FRP".[4]

El següent programa mostra la posició del ratolí en moure'l per la pantalla.

main = lift asText Mouse.position

El valor de main es mostra en pantalla. La funció asText converteix qualsevol valor en un valor textual representable. La funció lift assegura que asText serà aplicada al senyal Mouse.position a cada canvi del valor.

Elm té un petit però expressiu conjunt de construccions de llenguatge i tractament de llistes. També té un sistema de mòduls i un model d'interfase amb funcions foranes (FFI) per al JavaScript.

Les funcions predefinides són al mòdul Prelude.[5]

Valors[modifica]

Els valors són variables o expressions immutables. Entre els seus tipus tenim:

Senyals (variabilitat)[modifica]

(Signal tipusDelValor) és el tipus de les variables o expressions que canvien de valor, amb el temps (continus) o per l'efecte d'esdeveniments (discrets).

-- obtenir el temps actual, actualitzat cada t
Time.every : Time -> Signal Time

-- obtenir la posició del ratolí
Mouse.position : Signal (Int,Int)

Els elements de formularis HTML poden variar en estil i en estat. Les funcions que els generen tornen majoritàriement un parell de senyals (senyal_de_l'element, senyal_de_l'estat)[13] com a

-- crear una casella de marcar, amb el valor inicial indicat.
-- retorna el parell (senyal de l'estructura, senyal de l'estat)
checkbox : Bool -> (Signal Element, Signal Bool)

Senyals dependents[modifica]

Els senyals dependents són com les cel·les d'un full de càlcul que contenen una fórmula, i el seu valor es recalcula per reacció als canvis dels seus operands.

Es defineixen, o bé amb una expressió de filtre de senyals, o bé per l'aplicació d'una funció de valors, que per aplicar-la a senyals cal elevar-ne el tipus amb lift.

Per emprar un valor en un paràmetre de senyal, cal elevar-ne el tipus convertint-lo a senyal amb la funció Signal.constant.

Si la funció arrel (d'engegada), anomenada main, és un senyal, llavors s'exploraran les dependències entre senyals per obtenir-ne els independents i incloure'n els generadors en el bucle de despatx inicial.

Cada senyal manté una llista dels senyals que en depenen i que farà recalcular en cas que se li actualitzi el valor. No hi pot haver cicles en la relació de dependència.

Des de la perspectiva del llenguatge Haskell[modifica]

Senyal com a instància de la classe Functor[modifica]

Vegeu signatura de la classe Functor a Haskell.[14] De la biblioteca Signal d'Elm:

-- elevar una funció de ''valors'' a una funció de ''senyals''
lift : (a -> b) -> Signal a -> Signal b

-- (<~) és un àlies per a lift
Senyal com a instància de la classe Applicative[modifica]

Vegeu signatura de la classe Applicative a Haskell.[15] De la biblioteca Signal d'Elm:

-- elevar un valor
constant : a -> Signal a

-- aplicació seqüencial de funció de senyals
(~) : Signal (a -> b) -> Signal a -> Signal b

-- mostreja el segon senyal seqüencialment a un canvi de valor del primer.
sampleOn : Signal a -> Signal b -> Signal b

----------------------
-- per elevar una funció de valors de N paràmetres (definides lift2 a lift8)
senyal_resultant = liftN fun_N_ària senyal1 senyal2 ... senyalN

-- equivalent amb operadors binaris en infix
senyal_resultant = fun_N_ària <~ senyal1 ~ senyal2 ~ ... ~ senyalN

Transformadors de senyals componibles. Autòmats[modifica]

És un tipus quina finalitat és possibilitar definir una seqüència de transformacions en cadena (amb possibles efectes col·laterals).

El concepte és originari del TAD Fletxa de Haskell que descriu la seqüenciació d'efectes col·laterals mitjançant l'encadenament de morfismes.

La baula d'aquest encadenament és el tipus Automaton, que designa una aplicació de valors (amb possibles efectes col·laterals) amb un paràmetre d'origen i un resultat. El tipus és polimòrfic amb dos paràmetres, el tipus de l'entrada i el de la sortida (Automaton input output).

Per encadenar-ne dues, el tipus de la sortida de l'autòmata precedent ha de coincidir amb el tipus de l'entrada del següent.

Podem generar-ne a partir d'una funció pura (que només depèn dels paràmetres) o d'una amb memòria. De la biblioteca Automaton d'Elm:[16]

-- generador des d'una funció pura
pure : (a -> b) -> Automaton a b 

-- generadors a partir d'un estat inicial i una funció de l'entrada i de l'estat
state : b -> (a -> b -> b) -> Automaton a b 
hiddenState : s -> (a -> s -> (s,b)) -> Automaton a b

En podem fer un transformador de senyals amb la funció Automaton.run i un valor per defecte.

run : Automaton a b -> b -> Signal a -> Signal b

-- cal aportar un resultat per defecte, per al cas de manca de senyal d'entrada
senyalResultant = run elMeuAutòmata resultatPerDefecte senyalD'entrada

Els Automatons són componibles:

-- Compon dos autòmats mitjançant l'encadenament
(>>>) : Automaton a b -> Automaton b c -> Automaton a c

...

En realitat aquests autòmats componibles són una versió particular del concepte de les Fletxes de Haskell.[17][4]

Contenidors[modifica]

  • List, Set, Dict.[18]
  • Maybe (paràmetres opcionals i resultats de funcions definides parcialment, com a Nothing, o bé Just v)
  • Either (resultats de funcions definides parcialment amb informació de l'error, com a Right resultat o bé Left error)

Per processar una llista de senyals en un mateix paràmetre:

-- combina una llista de senyals en un senyal de la llista de valors
Signal.combine : [Signal a] -> Signal [a]

-- actualitza amb la primera que varia (cas de simultaneitat, la primera de la llista)
merges : [Signal a] -> Signal a

Eines[modifica]

mkdir elm-compiler && cd elm-compiler
cabal-dev install elm elm-server

export PATH=$PWD/cabal-dev/bin:$PATH

Exemples de Valors (sense variabilitat)[modifica]

Text estilitzat[modifica]

import Graphics.Element as Elem -- importació qualificada

unstyledText : Text
unstyledText = Text.toText "test1"

styleIt : Text -> Text
styleIt = (Text.typeface "serif"). (Text.color red) 

-- (f <| x = f x), evita l'ús de parèntesis

alignTest : Int -> Element
alignTest commonWidth = 
 let elem1 = Elem.width commonWidth <| Text.text <| styleIt unstyledText
 elem2 = Elem.width commonWidth <| Text.centered <| styleIt <| Text.toText "test2" 
 elem3 = Elem.width commonWidth <| Text.righted <| styleIt <| Text.toText "test3"

 in flow down [elem1, elem2, elem3]

main : Element
main = alignTest 200

Podeu provar-ho a l'editor online amb compilació i execució.

Polimorfisme en tipus registres[modifica]

Vegeu registres.[11]

module MyModule where

import Color

type Named a = { a | name : String } -- registres que tinguin el camp ''name''

getName : Named a -> String
getName {name} = name

dude = {name="John Doe", age=20}
lady = {name="Jane Doe", eyesColor = Color.blue}

names : [String]
names = [ getName dude, getName lady]

fullData = [show dude, show lady]

staticElement : Element
staticElement = flow down <| map plainText
 ["Names: " ++ show names
, show fullData
 ]
main = staticElement
  • incrustant-lo en un element div
<!DOCTYPE HTML>
<!-- MyModule.html -->
<html>
<head><meta charset="UTF-8">
 <title>MyModule</title>
 <!-- elm-runtime.js and js compiled modules -->
 <script type="text/javascript" 
 src="/your-install-directory/cabal-dev/share/Elm-N.N.N.N/elm-runtime.js"></script>
 <script type="text/javascript" src="MyModule.js"></script>
</head>
<body>
 <div id="myId" style="width:100;height:100;"></div> <!-- cal que el contenidor sigui un DIV i que estigui buit -->

 <script type="text/javascript">

Elm.embed(Elm.MyModule, document.getElementById('myId'))

</script>
 <noscript>Javascript is not enabled</noscript>
</body></html>
  • compilació i prova offline
# compila a Javascript
$ elm --make -s --only-js MyModule.elm
# executa
$ browser MyModule.html

Parametritzant un programa Elm[modifica]

La clàusula port per a la interfície FFI (Foreign function interface) amb Javascript[19] es pot aprofitar per proporcionar paràmetres a nivell de html.

module ElmMain where

port arg1 : String
port arg2 : Int
port arg3 : [String]

implode : [String] -> String
implode = concat. intersperse ", "

main = asText <| implode [arg1, show arg2, implode arg3]
<!DOCTYPE HTML>
<html>
<head><meta charset="UTF-8">
 <title>Title</title>
 <!-- elm-runtime.js i els mòduls compilats js
 (si es compila amb --make el ElmMain.js contindrà els mòduls d'usuari que s'importin -->

 <script type="text/javascript" 
 src="/your-install-directory/cabal-dev/share/Elm-N.N.N.N/elm-runtime.js"></script>
 <script type="text/javascript" src="ElmMain.js"></script>
</head>
<body>
 <div id="myId" style="width:100;height:100;"></div><!-- Elm container must be a "div" element and must be empty -->
 <script type="text/javascript">

var myPorts = {arg1: "do re mi", // de la lletra de la cançó "abc" dels "Jackson's 5"
 arg2: 123,
 arg3: ["abc", "you and me"]
 } ;

var myContainer = document.getElementById('myId'); 

Elm.embed(Elm.ElmMain, myContainer, myPorts) ;
 </script>
 <noscript>Javascript is not enabled</noscript>
</body></html>

Exemples de Senyals (amb variabilitat)[modifica]

Gràfics que varien al Tictac[modifica]

Fent servir la biblioteca Graphics.Collage

myShape : Shape
myShape1 = circle 30
myShape2 = rect 60 60

myForm1 : Form
myForm1 = outlined (dashed green) myShape1
myForm2 = outlined (solid red) myShape2

formes : [Form]
formes = [myForm1 |> move (10, -10)
, myForm2 |> move (30, -30) 
 |> rotate (degrees 45)
 |> scale 1.5

, plainText "mytext"
 |> toForm
 |> move (20, -20)
 |> rotate (degrees 30)
 |> scale 2
 ]

mapRotate : Float -> [Form] -> [Form]
mapRotate t = let f = rotate <| degrees <| t * 10
 in map f 

f >> g = g. f 

-- senyal de temps en segons, sense decimals 
tictac : Signal Float
tictac = let f = inSeconds >> truncate >> Prelude.toFloat
 in every (2 * second) 
 |> lift f

main : Signal Element
main = constant formes
 |> lift2 mapRotate tictac
 |> lift3 collage (constant 200) (constant 200)

Comprovador de contrasenya en camp doblat, amb confirmació per canvi de color[modifica]

import Graphics.Input as Input
import Graphics.Element as Elem
import String as Str

-- comprovador amb modificador de color
passwdOkColour : String -> String -> Element -> Element
passwdOkColour passwd1 passwd2 = 
 if passwd1 == passwd2 && Str.length passwd1 >= 6 
 then Elem.color green 
 else Elem.color red

display field1Elem field2Elem submitButtonElem =
 field1Elem `above` field2Elem `above` submitButtonElem

dynamicElement : Signal Element
dynamicElement = 
 let (field1ElemSignal, fld1StateSignal) = Input.password "Type password (min. 6 characters)!"
 labeledField1Signal = let prependLabel = beside (plainText "passwd: ")
 in lift prependLabel field1ElemSignal


 (field2ElemSignal, fld2StateSignal) = Input.password "Retype password!"
 labeledField2Signal = let prependLabel = beside (plainText "control: ")
 in lift prependLabel field2ElemSignal

 (submitButton, pressedSignal) = Input.button "Submit"

 coloredButSignal = constant submitButton 
 |> lift3 passwdOkColour fld1StateSignal fld2StateSignal 

 in lift3 display labeledField1Signal labeledField2Signal coloredButSignal

main = dynamicElement

Vegeu també[modifica]

Referències[modifica]

  1. «Operadors d'aplicació». Arxivat de l'original el 2013-06-07. [Consulta: 25 juliol 2013].
  2. Wan, Zhanyong; Taha, Walid; Hudak, Paul «Event-Driven FRP». Proceedings of the 4th International Symposium on Practical Aspects of Declarative Languages, 2002, pàg. 155–172.
  3. «Elm - What is Functional Reactive Programming?». Arxivat de l'original el 2018-03-23. [Consulta: 30 juliol 2013].
  4. 4,0 4,1 Elm - The Libraries You Need Arxivat 2016-03-04 a Wayback Machine. Automatons
  5. 5,0 5,1 5,2 «Biblioteca Prelude». Arxivat de l'original el 2013-09-05. [Consulta: 30 juliol 2013].
  6. «Char library». Arxivat de l'original el 2016-03-04. [Consulta: 30 juliol 2013].
  7. «Text library». Arxivat de l'original el 2013-08-17. [Consulta: 30 juliol 2013].
  8. «Date library». Arxivat de l'original el 2012-12-14. [Consulta: 30 juliol 2013].
  9. «Element library». Arxivat de l'original el 2013-09-05. [Consulta: 30 juliol 2013].
  10. «Collage library». Arxivat de l'original el 2013-08-17. [Consulta: 30 juliol 2013].
  11. 11,0 11,1 «Registres extensibles». Arxivat de l'original el 2013-04-15. [Consulta: 30 juliol 2013].
  12. «Sintaxi de Elm - Tipus de dades algebraics». Arxivat de l'original el 2016-03-13. [Consulta: 30 juliol 2013].
  13. «Graphics.Input library». Arxivat de l'original el 2013-08-17. [Consulta: 30 juliol 2013].
  14. «Haskell class Functor def.». Arxivat de l'original el 2013-06-09. [Consulta: 30 juliol 2013].
  15. «Haskell class Applicative def.». Arxivat de l'original el 2013-12-03. [Consulta: 30 juliol 2013].
  16. «Biblioteca Automaton». Arxivat de l'original el 2013-06-13. [Consulta: 30 juliol 2013].
  17. Haskell Arrows introduction
  18. «Biblioteques». Arxivat de l'original el 2013-08-11. [Consulta: 30 juliol 2013].
  19. «JavaScript FFI». Arxivat de l'original el 2016-03-13. [Consulta: 30 juliol 2013].

Enllaços externs[modifica]