Přejít k navigační liště

Zdroják » Různé » Velmi rychlý přehled Elm

Velmi rychlý přehled Elm

Články Různé

Elm je funkcionální reaktivní jazyk, který se kompiluje do (klientského) JavaScriptu. Elm je silně typovaný, díky tomu je překladač schopen zachytit většinu chyb okamžitě a vypsat snadno srozumitelná chybová hlášení. Elm se hodí k tvorbě webových uživatelských rozhraní a her.

Nálepky:

Článek je překladem anglického originálu Learn X in Y minutes WHERE X=Elm (CC BY-SA 3.0), jehož autorem je Max GoldSteing. Překlad Robina Pokorného najdete i na GitHubu.  

Naučte se Elm

-- Jednořádkové komentáře začínají dvěma pomlčkami.
{- Víceřádkové komentáře mohou být takto uzavřeny do bloku.
{- Mohou být i zanořeny. -}
-}
{-- Základy --}

-- Aritmetika
1 + 1 -- 2
8 - 1 -- 7
10 * 2 -- 20

-- Každé číslo bez desetinné tečky je typu Int nebo Float.
33 / 2 -- 16.5 s reálným dělením
33 // 2 -- 16 s celočíselným dělením

-- Umocňování
5 ^ 2 -- 25

-- Pravdivostní proměnné
not True -- False
not False -- True
1 == 1 -- True
1 /= 1 -- False
1 < 10 -- True

-- Řetězce a znaky
"Toto je textový řetězec, protože používá dvojité uvozovky."
'a' -- znak v jednoduchých uvozovkách

-- Řetězce lze spojovat.
"Ahoj " ++ "světe!" -- "Ahoj světe!"
{-- Seznamy (List), n-tice (Tuple) a Záznamy (Record) --}

-- Každá položka seznamu musí být stejného typu.
["příliš", "žluťoučký", "kůň", "úpěl"]
[1, 2, 3, 4, 5]
-- Druhý příklad lze zapsat také pomocí dvou teček.
[1..5]

-- Spojovat seznamy lze stejně jako řetězce.
[1..5] ++ [6..10] == [1..10] -- True

-- K přidání položky do seznamu použijte funkci "cons".
0 :: [1..5] -- [0, 1, 2, 3, 4, 5]

-- Funkce "head" pro získání první položky seznamu i funkce "tail" pro získání následujích položek
-- vrací typ Maybe. Místo zjišťování, jestli nějaká položka není null,
-- se s chybějcími hodnotami vypořádáme explicitně.
List.head [1..5] -- Just 1
List.tail [1..5] -- Just [2, 3, 4, 5]
List.head [] -- Nothing
-- List.nazevFunkce odkazuje na funkci, která žije v modulu List.

-- Každý prvek v n-tici může být jiného typu, ale n-tice má pevný počet prvků.
("elm", 42)

-- K získání hodnot z dvojice použijte funkce first a second.
-- (Toto je pouze zkratka. Brzy si ukážeme, jak na to "správně".)
fst ("elm", 42) -- "elm"
snd ("elm", 42) -- 42

-- Prázná n-tice, neboli "unit", se občas používá jako zástupný symbol.
-- Je to jediná hodnota svého typu, který se také nazývá "Unit".
()

-- Záznamy jsou podobné n-ticím, ale prvky jsou pojmenovány. Na pořadí nezáleží. 
-- Povšimněte si, že hodnoty vlastností se přiřazují rovnítky, ne dvojtečkami.
{ x = 3, y = 7 }

-- K hodnotám se přistupuje pomocí tečky a názvu vlastnosti.
{ x = 3, y = 7 }.x -- 3

-- Nebo využitím přístupové funkce, což je jen tečka a název vlastnosti.
.y { x = 3, y = 7 } -- 7

-- Změna hodnoty vlastnosti v záznamu. (Záznam tuto vlastnost už musí mít.)
{ osoba |
  jmeno = "Jiří" }

-- Změna více vlastností s využitím aktuálních hodnot.
{ hmotnyBod |
  poloha = hmotnyBod.poloha + hmotnyBod.rychlost,
  rychlost = hmotnyBod.rychlost + hmotnyBod.zrychleni }
{-- Řídicí struktury --}

-- Podmínky vždy musí mít větev "else" a obě větve musí být stejného typu.
if powerLevel > 9000 then
  "PÁNI!"
else
  "hmm"

-- Podmínky lze skládat za sebe.
if n < 0 then
  "n je záporné"
else if n > 0 then
  "n je kladné"
else
  "n je nula"

-- Použíjte příkaz "case" k nalezení shody vzoru a různých možností.
case seznam of
  [] -> "odpovídá práznému seznamu"
  [x]-> "odpovídá seznamu o právě jedné položce, " ++ toString x
  x::xs -> "odpovídá seznamu o alespoň jedné položce, jehož prvním prvkem je " ++ toString x
-- Shody se vyhodnocují v zapsaném pořadí. Kdybychom umístili [x] poslední, nikdy by nenastala shoda,
-- protože x::xs také odpovídá  (xs by byl prázdný seznam). Shody "nepropadají".
-- Překladač vždy upozorní na chybějící nebo přebývající větve.

-- Větvení typu Maybe.
case List.head seznam of
  Just x -> "První položka je " ++ toString x
  Nothing -> "Seznam byl prázdný."
{-- Funkce --}

-- Syntaxe funkcí je v Elmu velmi úsporná, založená spíše na mezerách
-- než na závorkách. Neexistuje tu klíčové slovo "return".

-- Funkci definujeme jejím jménem, parametry, rovnítkem a tělem.
vynasob a b =
  a * b

-- Funkci voláme předáním parametrů (bez oddělujících čárek).
vynasob 7 6 -- 42

-- Částečně aplikované funkci předáme pouzne některé parametry.
-- Poté zvolíme nové jméno.
zdvoj =
  vynasob 2

-- Konstanty jsou podobné, ale nepřijímají žádné parametry.
odpoved =
  42

-- Předejte funkci jako parametr jiným funkcím.
List.map zdvoj [1..4] -- [2, 4, 6, 8]

-- Nebo použijte anonymní funkci.
List.map (\a -> a * 2) [1..4] -- [2, 4, 6, 8]

-- V definici funkce lze zapsat vzor, může-li nastat pouze jeden případ.
-- Tato funkce přijímá jednu dvojici místo dvou parametrů.
obsah (sirka, delka) =
  sirka * delka

obsah (6, 7) -- 42

-- Složenými závorkami vytvořte vzor pro názvy vlastností v záznamu.
-- Použijte "let" k definici lokálních proměnných.
objem {sirka, delka, hloubka} =
  let
    obsah = sirka * delka
  in
    obsah * hloubka

objem { sirka = 3, delka = 2, hloubka = 7 } -- 42

-- Funkce mohou být rekurzivní.
fib n =
  if n < 2 then
    1
  else
    fib (n - 1) + fib (n - 2)

List.map fib [0..8] -- [1, 1, 2, 3, 5, 8, 13, 21, 34]

-- Jiná rekurzivní funkce (v praxi použijte List.length).
delkaSeznamu seznam =
  case seznam of
    [] -> 0
    x::xs -> 1 + delkaSeznamu xs

-- Funkce se volají před jakýmkoli infixovým operátorem. Závorky určují prioritu.
cos (degrees 30) ^ 2 + sin (degrees 30) ^ 2 -- 1
-- Nejprve se aplikuje "degrees" na číslo 30, výsledek je pak předán trigonometrickým
-- funkcím, které jsou následně umocněny na druhou, na závěr proběhne sčítání.
{-- Typy a typové anotace --}

-- Překladač odvodí typ každé hodnoty ve vašem programu.
-- Typy vždy začínají velkým písmenem. Čtete x : T jako "x je typu T".
-- Některé běžné typy, které můžete videt v Elmovém REPLu.
5 : Int
6.7 : Float
"ahoj" : String
True : Bool

-- Funkce mají také typy. Čtěte "->" jako "vrací".
-- O typu na konci uvažujte jako návratovém typu, o ostatních jako typech argumentů.
not : Bool -> Bool
round : Float -> Int

-- Když definujete hodnotu, je dobrým zvykem zapsat nad ni její typ.
-- Anotace je formou dokumentace, která je ověřována překladačem.
zdvoj : Int -> Int
zdvoj x = x * 2

-- Funkce jako parametr je uzavřena v závorkách.
-- Typy s malým počátečním písmenem jsou typové proměnné:
-- mohou být libovolného typu, ale v každém volání musí být stejné.
List.map : (a -> b) -> List a -> List b
-- "List tečka map je typu a-vrací-b, vrací seznam-položek-typu-a, vrací seznam-položek-typu-b."

-- Existují tři speciální typové proměnné:
-- číslo (number), porovnatelné (comparable), and spojitelné (appendable).
-- Čísla dovolují použít aritmetiku na Int a Float.
-- Porovnatelné dovolují uspořádat čísla a řetězce, např. a < b.
-- Spojitelné lze zřetězit pomocí a ++ b.
{-- Typové aliasy a výčtové typy --}

-- Pro záznamy a n-tice již typy automaticky existují.
-- (Povšimněte si, že typ vlatnosti záznamu přiřazujeme dvojtečkou a hodnotu rovnítkem.)
pocatek : { x : Float, y : Float, z : Float }
pocatek =
  { x = 0, y = 0, z = 0 }

-- Stávajícím typům lze dávat jména využitím aliasů.
type alias Bod3D =
  { x : Float, y : Float, z : Float }

-- Alias pro záznam funguje také jako jeho konstruktor.
jinyPocatek : Bod3D
jinyPocatek =
  Bod3D 0 0 0

-- Jedná se stále o stejný typ, lze je tedy porovnat.
pocatek == jinyPocatek -- True

-- Oproti tomu výčtový (union) typ definuje zcela nový typ.
-- Výčtový typ se takto jmenuje, protože může být jedním z několika vybraných možností.
-- Každá možnost je reprezentována jako "tag".
type Smer =
  Sever | Jih | Vychod | Zapad

-- Tagy mohou obsahovat další hodnoty známých typů. Lze využít i rekurze.
type IntStrom =
  Vrchol | Uzel Int IntStrom IntStrom
-- "Vrchol" i "Uzel" jsou tagy. Vše, co následuje za tagem, je typ.

-- Tagy lze použít jako hodnoty funkcí.
koren : IntStrom
koren =
  Vrchol 7 List List

-- Výčtové typy (a typové aliasy) mohou obsahovat typové proměnné.
type Strom a =
  Vrchol | Uzel a (Strom a) (Strom a)
-- "Typ strom-prvků-a je vrchol, nebo uzel obsahující a, strom-prvků-a a strom-prvků-a."

-- Vzory se shodují s tagy. Tagy s velkým počátečním písmenem odpovídají přesně.
-- Proměnné malým písmem odpovídají čemukoli. Podtržítko také odpovídá čemukoli,
-- ale určuje, že tuto hodnotu dále nechceme používat.
nejviceVlevo : Strom a -> Maybe a
nejviceVlevo strom =
  case strom of
    Vrchol -> Nothing
    Uzel x Vrchol _ -> Just x
    Uzel _ podstrom _ -> nejviceVlevo podstrom

-- To je víceméně vše o jazyku samotném.
-- Podívejme se nyní, jak organizovat a spouštět náš kód.
{-- Moduly a importování --}

-- Standardní knihovny jsou organizovány do modulů, stejně jako knihovny třetích stran,
-- které můžete využívat. Ve větších projektech můžete definovat vlastní moduly.

-- Vložte toto na začátek souboru. Pokud nic neuvedete, předpokládá se "Main".
module Jmeno where

-- Výchozím chováním je, že se exportuje vše.
-- Případně můžete definovat exportované vlastnosti explicitně.
module Jmeno (MujTyp, mojeHodnota) where

-- Běžný návrhový vzor je expotovat pouze výčtový typ bez jeho tagů.
-- Tento vzor je znám jako krycí typ a často se využívá v knihovnách.

-- Z jiných modulů lze importovat kód a použít jej v aktuálním modulu.
-- Následující umístí Dict do aktuálního scope, takže lze volat Dict.insert.
import Dict

-- Importuje modul Dict a typ Dict, takže v anotacích není nutné psát Dict.Dict.
-- Stále lze volat Dict.insert.
import Dict exposing (Dict)

-- Přejmenování importu.
import Graphics.Collage as C
{-- Porty --}

-- Port oznamuje, že budete komunikovat s vnějším světem.
-- Porty jsou dovoleny pouze v modulu Main.

-- Příchozí port je jen typová anotace.
port idKlienta : Int

-- Odchozí port má definici.
port objednavkaKlienta : List String
port objednavkaKlienta = ["Knihy", "Potraviny", "Nábytek"]

-- Nebudeme zacházet do detailů, ale v JavaScriptu se dají nastavit
-- callbacky pro zasílání na příchozí porty a čtení z odchozích portů.
{-- Nástroje pro příkazovou řádku --}

-- Kompilace souboru.
$ elm make MujSoubor.elm

-- Při prvním spuštění nainstaluje Elm standardní knihovny a vytvoří soubor
-- elm-package.json, kde jsou uloženy informace o vašem projektu.

-- Elm reactor je server, který překládá a spouští vaše soubory.
-- Kliknutím na klíč vedle názvu souboru spustíte debugger s cestováním v čase!
$ elm reactor

-- Zkoušejte si jednoduché příkazy v Read-Eval-Print Loop.
$ elm repl

-- Balíčky jsou určeny uživatelským jménem na GitHubu a názvem repozitáře.
-- Nainstalujte nový balíček a uložte jej v souboru elm-package.json.
$ elm package install evancz/elm-html

-- Porovnejte změny mezi verzemi jednoho balíčku.
$ elm package diff evancz/elm-html 3.0.0 4.0.2
-- Správce balíčků v Elmu vyžaduje sémantické verzování, 
-- takže minor verze nikdy nerozbije váš build.

Jazyk Elm je překvapivě malý. Nyní se můžete podívat do skoro jakéhokoli zdrojového kódu v Elmu a budete mít zběžnou představu o jeho fungování. Ovšem možnosti, jak psát kód, který je odolný vůči chybám a snadno se refaktoruje, jsou neomezené!

Zde jsou některé užitečné zdroje (v angličtině).

Český rozcestník Elm najdete na elm-lang.cz.

Běžte si zkusit něco napsat v Elmu!

Komentáře

Subscribe
Upozornit na
guest
2 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
petrbolf

Super, dík za článek (překlad).

Ještě doplním odkaz na tutoriál komplexnější CRUD aplikace.

Pro zajímavost, jak se dívat na výsledný zkompilovaný javascript? Je lepší ho brát jako black box a moc se nezajímat o to, co se tam děje?

První pohled mne trochu vyděsil, obyčejný hello world vytvoří 10 000 řádků javascriptového kódu a i ten text se do stránky vypíše javascriptem a ne přímo do html . Na druhý pohled je to už lepší, jak jsem se díval, většina toho kódu je implementace virtual domu (nenačítá externí knihovnu, má to celé v sobě) a pak spousta objektů a funkcí, které odpovídají různým prvkům jazyka Elm (jako objekty Elm.Native, Elm.Native.Graphics, Elm.Graphics, …. a k nim funkce Elm.Native.make, Elm.Native.Graphics.make, Elm.Graphics.make, …)

On ten kód je poměrně čitelný, není minifikovaný a je pěkně odsazený (až na některé „knihovní“ pasáže zřejmě souvisejicí s virtual domem) a tak není až takový problém pochopit co se tam děje.

To jen tak ze zvědavosti. Chápu, to je asi jako programovat v Javě a zkoumat bytekod a nebo programovat v c++ a zkoumat assembler.

A pak je tu ještě část v Haskellu, která ten elmový zdroják překládá do toho javascriptu. Také zajimavé téma na dlouhé zimní večery :-)

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.