Elm (llenguatge de programació)

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]