Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi

18. 10. 2016
Doba čtení: 20 minut

Sdílet

Clojure vyžaduje virtuální stroj JVM, CLR či VM pro JavaScript. Žádná z těchto variant není vhodná pro psaní kratších skriptů, ovšem existuje řešení: jazyk Pixie s vlastní „odlehčenou“ VM.

Obsah

1. Clojure pro JVM, CLR i virtuální stroje JavaScriptu

2. Programovací jazyk Pixie aneb „Clojure bez JVM“

3. Překlad interpretru a JITu jazyka Pixie

4. Kontrola, zda jsou nainstalovány všechny potřebné knihovny

5. Spuštění interpretru (smyčky REPL)

6. Strukturované datové typy (kolekce)

7. Základní operace nad strukturovanými datovými typy (kolekcemi)

8. Funkce vyššího řádu

9. Threading makra

10. Podpora pro práci se zlomky a čísly s neomezeným rozsahem

11. Práce s atomy

12. Atomy a strukturované datové typy

13. Odkazy na předchozí části tohoto seriálu

14. Odkazy na Internetu

1. Clojure pro JVM, CLR i virtuální stroje JavaScriptu

„That is to say, if you're doing lightweight scripting, or want something that boots fast and runs well on low-end systems, Pixie might be a good fit. Basically I want it for all those times I'd reach for Python instead of Clojure.“
Autor programovacího jazyka Pixie.

Programovací jazyk Clojure v současnosti oficiálně existuje ve třech variantách. Pravděpodobně nejpoužívanější varianta je určena pro běh nad JVM (virtuálním strojem jazyka Java, Java Virtual Machine), dále existuje varianta pro CLR (Common Language Runtime z .NET) a třetí variantou je ClojureScript transformovaný do JavaScriptu a tedy využitelný například ve webových prohlížečích či v běhovém prostředí Node.js. Všechny tři zmíněné varianty mají jednu společnou vlastnost – spuštění a inicializace vlastního virtuálního stroje je poměrně zdlouhavé, což je kritické zejména na méně výkonných zařízeních (IoT). To mj. znamená, že se Clojure (ale například ani Java, Jython či Ruby) příliš často nepoužívá k psaní nástrojů, které mají běžet relativně krátkou dobu a mají být spouštěny z příkazové řádky. Existují sice řešení, které se snaží neustálé spouštění virtuálního stroje obejít (dobrým příkladem je projekt Nailgun), ovšem to neřeší vlastní problém; ten se pouze více či méně úspěšně obchází.

2. Programovací jazyk Pixie aneb „Clojure bez JVM“

„…the first language I ever wrote was when I was in high-school. It was a small interpreted language named PhaLinks and was terrible, but I learned a lot from the effort. I also acquired a distaste for parsers in the process. Which, incidentally, is why I stick with Lisp these days: Lisp parsers are super easy to write.“
Autor programovacího jazyka Pixie.

Pokud programátoři, kteří propadli kouzlu programovacího jazyka Clojure, potřebují kromě aplikací běžících delší dobu (webový server atd.) vytvářet i kratší skripty spouštěné typicky z příkazové řádky, nezbývá jim nic jiného, než se poohlédnout po alternativních řešeních a jazycích, které se (alespoň prozatím) Clojure pouze podobají a nejsou s ním vždy na 100% kompatibilní. Prozatím pravděpodobně nejpropracovanější variantou je jazyk nazvaný Pixie. Jazyk Pixie využívá just in time překladač (JIT) i správce paměti implementovaný v PyPy, další části Pixie jsou pak psány v jazycích C a C++.

Na rozdíl od Clojure, které se spoléhá na to, že programátoři budou využívat knihovny kompatibilní s vybraným virtuálním strojem (tj. například javovskou standardní knihovnu apod.) se jazyk Pixie spoléhá na rozhraní FFI (Foreign Function Interface) a tím pádem na použití nativních knihoven dostupných v operačním systému, v němž aplikace poběží. Kombinace JITu a GC z PyPy společně s nativními knihovnami se ukazuje být úspěšná, protože skripty psané v Pixie bez výkonnostních problémů fungují i na prvních modelech Raspberry Pi (to se týká i prakticky okamžitého spouštění).

Poznámka: Pixie není v žádném případě prvním programovacím jazykem, který se do jisté míry snaží napodobit programovací jazyk Clojure. V tomto seriálu jsme se již setkali s programovacím jazykem Wisp, který je automaticky s využitím takzvaného transpileru transformován do JavaScriptu, dále existuje sémanticky podobný jazyk naprogramovaný v Perlu atd. Ovšem Pixie je z těchto jazyků nejpropracovanější a především pak prakticky využitelný.

3. Překlad interpretru a JITu jazyka Pixie

Všechny dále uvedené příklady byly spouštěny a odzkoušeny na poslední verzi jazyka Pixie přeloženého ze zdrojových kódů získaných na GitHubu z repositáře https://github.com/pixie-lang/pixie (větev master). Vzhledem k tomu, že se tento jazyk poměrně rychle vyvíjí, je lepší provést překlad Pixie právě z oficiálního repositáře a nepoužívat velmi pravděpodobně zastaralou verzi z vaší linuxové distribuce. Ve skutečnosti není překlad příliš složitý, pouze poměrně zdlouhavý. Navíc vám k urychlení překladu příliš nepomůže procesor s větším množstvím jader, protože podstatná část překladu probíhá v jediném vláknu.

Prvním krokem je instalace všech potřebných knihoven:

  1. libffi-dev
  2. libedit-dev
  3. libuv-dev
  4. libboost-dev

Dále je nutné, aby byl na systému nainstalován interpret Pythonu (2), což je však s velkou pravděpodobností na prakticky všech distribucích splněno.

Další závislosti:

  1. Překladač jazyka C (gcc)
  2. Překladač jazyka C++ (g++)
  3. Nástroj Make

Dejte si prosím pozor na to, že pokud není nainstalován překladač jazyka C++, může překlad vypadat zdánlivě v pořádku, protože chybová hlášení relativně rychle „odscrollují“, ovšem překlad pokračuje dále.

O vlastní překlad se postará nástroj Make, překlad se spustí takto:

make build_with_jit

V první fázi překladu dochází ke stažení dalších balíčků potřebných pro sestrojení virtuálního stroje jazyka Pixie. Nelekněte se tedy, že se stahuje například poměrně objemný tarball obsahující PyPy z adresy https://bitbucket.org/pypy/py­py/get/91db1a9.tar.bz2.

V průběhu překladu dochází mj. i k ladění parametrů JITu, takže se nelekněte, když se při překladu začnou na terminál vykreslovat různé fraktály atd. (viz též přiložené screenshoty).

Výsledkem překladu by měl být spustitelný soubor nazvaný pixie-vm o velikosti přibližně 11 MB (což je stále o mnoho méně, než je velikost JVM z JRE). Pro úspěšné spuštění interpretru dále potřebujete adresář pixie; ostatní adresáře jsou potřebné jen pro otestování či pro překlad.

4. Kontrola, zda jsou nainstalovány všechny potřebné knihovny

Typický problém, na který můžete narazit až po překladu interpretru, je chybějící knihovna libedit-dev. Pokud tato knihovna není nainstalována (skutečně se jedná o -dev variantu), vypíše se při pokusu o start interpretru následující chybové hlášení:

./pixie-vm
 
in internal function run_load_stdlib
 
in internal function load-ns
 
in internal function load-file
 
in pixie function _toplevel_
 
in pixie/stdlib.pxi at 14:14
(def libedit (ffi-library (str "libedit." pixie.platform/so-ext)))
              ^
in internal function ffi-library
 
RuntimeException: <inst pixie.stdlib.Keyword> Couldn't Load Library: libedit.so

Podobné chybové hlášení se objeví ve chvíli, kdy není k dispozici knihovna libffi-dev. Ostatní závislosti jsou kontrolovány již v době překladu.

5. Spuštění interpretru (smyčky REPL)

Předpokládejme, že se virtuální stroj s interpretrem jazyka Pixie podařilo přeložit bez problémů. Zbývá nám si otestovat jeho základní funkcionalitu, takže si nejprve interpret spusťme:

./pixie-vm
user =>

Spuštěním virtuálního stroje jsme se dostali do základního režimu interpretru – do interaktivní smyčky REPL (Read Eval Print Loop), v níž lze zadávat libovolné formy, které jsou ihned interpretovány. Syntaxe zápisu forem odpovídá syntaxi programovacího jazyka Clojure, což si ostatně můžeme velmi jednoduše vyzkoušet:

; vyhodnocení konstant
user => 42
42
 
user => :foo
:foo
 
user => "Hello"
"Hello"
 
user => 1/2
1/2
 
user => nil
nil
 
; volání funkcí vyhodnocujících základní aritmetické výrazy
user => (* 6 7)
42
 
user => (* (+ 1 2 3) (- 4 5 6))
-42
 
; většina funkcí ze standardní knihovny obsahuje dokumentační řetězce
user => (doc +)
pixie.stdlib/+
([& args])
 
Adds the arguments, returning 0 if no arguments.
 
nil
 
user => (doc println)
pixie.stdlib/println
([& args])
 
Prints the arguments, separated by spaces, with a newline at the end.
 
nil
 
user => (doc conj)
pixie.stdlib/conj
([] [coll] [coll item] [coll item & args])
 
Adds elements to the collection. Elements are added to the end except in the case of Cons lists.
 
nil

6. Strukturované datové typy (kolekce)

Programovací jazyk Pixie podporuje všechny základní strukturované datové typy, které známe z jazyka Clojure. Jedná se o seznamy, vektory, mapy i množiny:

# Typ kolekce Zápis (syntaktický cukr) Konstruktor
1 Seznam (prvky) '(prvky)
(list prvky)
2 Vektor [prvky] (vector prvky)
(vec kolekce)
3 Mapa {dvojice klíč-hodnota} (hash-map dvojice klíč-hodnota)
(sorted-map dvojice klíč-hodnota)
4 Množina #{unikátní prvky} (hash-set unikátní prvky)
(sorted-set unikátní prvky)

Konstruktory těchto typů zapisované s využitím „syntaktického cukru“ jsou taktéž shodné, ostatně se podívejme na příklady:

user => (def seznam '(1 2 3 'a 'b "hello"))
<inst pixie.stdlib.Var>
 
user => (type seznam)
pixie.stdlib.Cons
 
user => (def vektor [1 2 3 'a 'b "hello"])
<inst pixie.stdlib.Var>
 
user => (type vektor)
pixie.stdlib.PersistentVector
 
user => (def mapa {:first "jedna" :second "dva"})
<inst pixie.stdlib.Var>
 
user => (type mapa)
pixie.stdlib.PersistentHashMap
 
user => (def mnozina #{1 2 3})
<inst pixie.stdlib.Var>
 
user => (type mnozina)
pixie.stdlib.PersistentHashSet

Všechny tyto strukturované typy jsou neměnitelné (immutable), stejně jako je tomu v programovacím jazyku Clojure. Ostatně se jedná o jednu ze základních vlastností Clojure, která je – alespoň podle názoru autora tohoto článku – jedním z důležitých faktorů oblíbenosti tohoto jazyka.

Mezi další vlastnosti, které jsou společné všem kolekcím, patří:

  • Kolekce jsou perzistentní.
  • Mezi libovolným počtem kolekcí jazyk zajišťuje sdílení struktury, pokud je to možné.
  • U všech kolekcí lze zjistit počet prvků funkcí count.
  • Každá kolekce je mapovatelná na sekvenci a je tedy možné procházet všemi prvky kolekce.
  • Ke každé kolekci je možné „přidat“ nový prvek funkcíconj, výsledkem je nová kolekce.

7. Základní operace nad strukturovanými datovými typy (kolekcemi)

Některé funkce nabízené jazykem Pixie jsou společné všem kolekcím. Ty nejužitečnější funkce jsou vypsány v následující tabulce. Povšimněte si, že některé funkce známé z jazyka Clojure, například funkce peek, zde nenajdeme (ovšem není problém si je v případě potřeby dodefinovat):

# Funkce Význam funkce
1 count vrátí počet prvků v kolekci
2 empty? (s otazníkem na konci) vrátí true v případě, že je kolekce prázdná
3 empty (bez otazníku) vrátí prázdnou kolekci stejného typu
4 not-empty? pokud není parametrem prázdná kolekce, vrátí se tato kolekce, jinak se vrátí nil
5 cons vrátí novou kolekci s přidaným prvkem (odkaz jazyka LISP)
6 pop vrátí kolekci bez prvního prvku (seznamy) nebo bez prvku posledního (vektory)
7 nth získání n-tého prvku kolekce
8 first první prvek kolekce
9 rest kolekce bez prvního prvku

Tyto funkce si můžeme snadno vyzkoušet v interaktivní smyčce REPL:

user => (empty? [1 2 3])
false
 
user => (empty? [])
true
 
user => (empty? {})
true
 
user => (empty? {:prvni 1 :druhy 2})
false
 
; speciální případ, kdy nil nahrazuje prázdnou kolekci
user => (empty? nil)
true
 
user => (count [1 2 3])
3
 
user => (def seznam '(1 2 3))
<inst pixie.stdlib.Var>
 
user => (def vektor [1 2 3])
<inst pixie.stdlib.Var>
 
user => (conj seznam 100)
(100 1 2 3)
 
user => (conj vektor 100)
[1 2 3 100]
 
user => (pop [1 2 3])
[1 2]
 
user => (nth [1 2 3] 1)
2
 
user => (first mnozina)
2
 
user => (first vektor)
1
 
user => (rest vektor)
(2 3)
 
user => (rest mnozina)
(3 1)

U map lze použít i zkrácený zápis pro přečtení prvku uloženého pod nějakým klíčem:

user => (def mapa {:first "jedna" :second "dva"})
<inst pixie.stdlib.Var>
 
user => (count mapa)
2
 
user => (get mapa :first)
"jedna"
 
user => (get mapa :blabla)
nil
 
user => (:first mapa)
"jedna"
 
user => (:foobar mapa)
nil

Další užitečnou funkcí je funkce into, která dokáže převádět prvky mezi různými typy kolekcí. Například následující příklad převede sekvenci dvojic čísel na mapu, v níž jsou klíčem celá čísla 1 až 9 a hodnotami druhé mocniny těchto klíčů:

user => (into {} (for [x (range 1 10)] [x (* x x)]))
{9 81, 8 64, 1 1, 3 9, 5 25, 2 4, 6 36, 7 49, 4 16}

8. Funkce vyššího řádu

V programovacím jazyku Pixie jsou samozřejmě podporovány i funkce vyššího řádu, ostatně jedná se o LISPovský jazyk, takže by se nemělo jednat o velké překvapení. Mezi základní funkce vyššího řádu patří zejména čtveřice map, apply, filter a reduce. Pojďme si nyní použití těchto funkcí otestovat.

Nejdříve vytvoříme sekvenci, přesněji řečeno speciální typ sekvence, pomocí funkce range:

user => (def a (range 1 10))
<inst pixie.stdlib.Var>
user => a
(1 2 3 4 5 6 7 8 9)
user => (type a)
Range

Funkce vyššího řádu reduce postupně aplikuje předanou funkci (zde konkrétně funkci +) na akumulovaný mezivýsledek a jednotlivé prvky sekvence (funkce musí akceptovat dva parametry). Sekvence je tedy postupně redukována v každé iteraci o jeden prvek:

user => (reduce + a)
45

Funkce apply naproti tomu převezme sekvenci (druhý parametr) a vytvoří z nich parametry funkce (první parametr). Výsledek sice bude v tomto případě stejný, ovšem pro jiné funkce to samozřejmě nemusí být pravda:

user => (apply + a)
45

Funkce vyššího řádu map dokáže pro každý prvek sekvence zavolat předanou funkci a vytvořit tak novou sekvenci. Zde konkrétně použijeme anonymní funkci (lambdu), která vynásobí svůj parametr dvěma:

user => (map #(* % 2) a)
(2 4 6 8 10 12 14 16 18)

Užitečná je i funkce nazvaná filter, která z předané sekvence vybere pouze ty prvky, které odpovídají zadanému predikátu. Predikátem je libovolná funkce vracející pravdivostní hodnotu true či false (popř. nil, který se taktéž chápe jako false). My si vytvoříme nový predikát, kterým bude funkce testující, zda je parametr liché číslo:

user => (defn odd [x] (= 1 (rem x 2)))
<inst pixie.stdlib.Var>
user => (odd 1)
true
user => (odd 2)
false

S využitím tohoto predikátu již můžeme z předané sekvence vyfiltrovat pouze liché prvky:

user => (filter odd a)
(1 3 5 7 9)

Poslední zajímavou a užitečnou funkcí vyššího řádu je funkce take-while. Tato funkce se podobá funkci take, která z předané sekvence získá prvních n prvků:

; nekonečná sekvence (ve skutečnosti je tato sekvence v jazyku Pixie omezena)
user => (def b (range))
 
user => (take 10 b)
(0 1 2 3 4 5 6 7 8 9)

Funkce take-while namísto hodnoty n očekává predikát. Dokud predikát vrací hodnotu true, budou se prvky ze vstupní sekvence předávat do sekvence výstupní, při první hodnotě false je funkce ukončena. Tímto způsobem lze například získat ta celá čísla, jejichž druhá mocnina je menší než 100 popř. 1000:

user => (take-while #(< (* % %) 100) b)
(0 1 2 3 4 5 6 7 8 9)
user => (take-while #(< (* % %) 1000) b)
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31)

Následují typické „školní“ příklady použití funkcí apply a reduce pro získání tabulky faktoriálů:

user => (doseq [n (range 1 11)] (println n (apply * (range 1 (inc n)))))
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800
nil
user => (doseq [n (range 1 11)] (println n (reduce * (range 1 (inc n)))))
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800
nil

9. Threading makra

V programovacím jazyku Clojure existuje dvojice velmi užitečných maker, která je možné použít pro zjednodušení zápisu „kolony“ funkcí, kterým se postupně předávají výsledky předchozích funkcí. Jedná se vlastně o obdobu klasické Unixové kolony používané při spojování filtrů v shellu (ls –1 | sort). V Clojure je možné namísto poměrně nečitelného zápisu (f (g (h x))) použít zápis (->x h g f), kterým se specifikuje, že se hodnota x předá funkci h, výsledek této funkce se předá funkci g a výsledek funkce g se předá funkci f.

Threading makro → přidá předchozí výsledek další funkci ve svém prvním parametru:

user => (def a (range 1 11))
<inst pixie.stdlib.Var>
 
user => a
(1 2 3 4 5 6 7 8 9 10)
 
user => (-> a count)
10
 
user => (-> a count inc)
11

V mnoha případech nám však nemusí vyhovovat, že se předchozí výsledek předá další funkci v koloně v prvním parametru. Dobrým příkladem mohou být již výše zmíněné funkce vyššího řádu. Pokud se tyto funkce používají v koloně, musí se namísto makra → použít druhé threading makro nazvané ->>, které předchozí výsledek předá další funkci v posledním parametru, nikoli v parametru prvním:

user => (->> a (map #(* % 2)))
(2 4 6 8 10 12 14 16 18 20)
 
user => (->> a (map #(* % 2)) (reduce +))
110
 
user => (->> a (filter #(< % 5)) (map #(* % 2)))
(2 4 6 8)

Druhý příklad na threading makro ->>, které provede tyto operace:

  1. Vytvoří sekvenci deseti hodnot 0..9
  2. Vyfiltruje ze sekvence lichá čísla
  3. Prvky výsledné sekvence 1, 3, .. 9 vynásobí dvěma
user => (defn odd[x] (= 1 (rem x 2)))
<inst pixie.stdlib.Var>
 
user => (->> 10 range (filter odd) (map #(* 2 %)))
(2 6 10 14 18)

10. Podpora pro práci se zlomky a čísly s neomezeným rozsahem

Podobně jako v naprosté většině ostatních LISPovských jazyků nalezneme i v programovacím jazyku Pixie podporu pro práci se zlomky (které tvoří samostatný datový typ, interně se jedná o dvojici celých čísel) i podporu pro práci s celočíselnými hodnotami s prakticky neomezeným rozsahem (rozsah je samozřejmě omezen kapacitou operační paměti, ovšem z praktického hlediska lze rozsah považovat za neomezený). Nejprve si ukažme práci se zlomky, které se zapisují tak, jak jsme zvyklí ze školy (znak „/“ zde samozřejmě neznamená operátor dělení):

user => 1/3
1/3
 
user => 1/2
1/2

Operace se zlomky se zapisují jednoduše. Povšimněte si, že po každé operaci se automaticky provede zjednodušení zlomku:

user => (+ 1/3 1/2)
5/6
 
user => (+ 1/2 1/4)
3/4
 
user => (* 2 (+ 1/2 1/4))
3/2
 
user => (+ 1/2 (* 3/4 (/ 5/6 7/8)))
17/14

Další příklad: kombinace threading makra, konstruktoru sekvence, funkce vyššího řádu reduce a zlomků:

user => (->> (repeat 10 3/7) (reduce *))
59049/282475249

Většinu problémů s datovým typem float/double lze tedy velmi snadno obejít právě použitím zlomků; samozřejmě však nesmíme zapomenout na to, že výpočty mohou být i řádově pomalejší.

Práce s celými čísly s neomezeným rozsahem je z pohledu programátora velmi jednoduchá. Jazyk Pixie se ve většině případů dokáže sám rozhodnout, kdy tento rozsah použít a kdy postačují prostá celá čísla (typu long integer). Povšimněte si odlišného typu následujících dvou hodnot:

user => (type 1)
pixie.stdlib.Integer
 
user => (type (* 10000000000 1000000000))
pixie.stdlib.BigInteger

Následuje poněkud extrémní příklad použití při výpočtu faktoriálu (namísto funkce apply lze použít i funkci reduce):

user => (type (apply * (range 1 1000)))
pixie.stdlib.BigInteger
 
user => (apply * (range 1 1000))
402387260077093773543702433923003985719374864210714632543799
910429938512398629020592044208486969404800479988610197196058
631666872994808558901323829669944590997424504087073759918823
627727188732519779505950995276120874975462497043601418278094
646496291056393887437886487337119181045825783647849977012476
632889835955735432513185323958463075557409114262417474349347
553428646576611667797396668820291207379143853719588249808126
867838374559731746136085379534524221586593201928090878297308
431392844403281231558611036976801357304216168747609675871348
312025478589320767169132448426236131412508780208000261683151
027341827977704784635868170164365024153691398281264810213092
761244896359928705114964975419909342221566832572080821333186
116811553615836546984046708975602900950537616475847728421889
679646244945160765353408198901385442487984959953319101723355
556602139450399736280750137837615307127761926849034352625200
015888535147331611702103968175921510907788019393178114194545
257223865541461062892187960223838971476088506276862967146674
697562911234082439208160153780889893964518263243671616762179
168909779911903754031274622289988005195444414282012187361745
992642956581746628302955570299024324153181617210465832036786
906117260158783520751516284225540265170483304226143974286933
061690897968482590125458327168226458066526769958652682272807
075781391858178889652208164348344825993266043367660176999612
831860788386150279465955131156552036093988180612138558600301
435694527224206344631797460594682573103790084024432438465657
245014402821885252470935190620929023136493273497565513958720
559654228749774011413346962715422845862377387538230483865688
976461927383814900140767310446640259899490222221765904339901
886018566526485061799702356193897017860040811889729918311021
171229845901641921068884387121855646124960798722908519296819
372388642614839657382291123125024186649353143970137428531926
649875337218940694281434118520158014123344828015051399694290
153483077644569099073152433278288269864602789864321139083506
217095002597389863554277196742822248757586765752344220207573
630569498825087968928162753848863396909959826280956121450994
871701244516461260379029309120889086942028510640182154399457
156805941872748998094254742173582401063677404595741785160829
230135358081840096996372524230560855903700624271243416909004
153690105933983835777939410970027753472000000000000000000000
000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000N

11. Práce s atomy

Programovací jazyk Pixie podporuje práci se dvěma typy symbolů, na nichž může být navázána hodnota. Pokud se použije deklarace ve stylu:

(def Pi 3.1415)

je vytvořen symbol, na nějž je navázána konstantní hodnota, kterou nelze změnit (samozřejmě je možné symbol předefinovat, to však vede k neidiomatickému kódu). Pokud je zapotřebí pracovat s měnitelnými proměnnými, lze v jazyku Pixie použít takzvané atomy, které mají k proměnným pravděpodobně nejblíže. Zatímco však běžné proměnné může měnit jakékoli vlákno bez další koordinace s ostatními vlákny, jsou hodnoty atomů modifikovány „atomicky“ – ostatní vlákna uvidí buď starou hodnotu nebo hodnotu novou, nikdy ne výsledek nějakého mezivýpočtu.

Práce s atomy je v praxi velmi jednoduchá, protože si programátor vystačí se čtveřicí funkcí, k nimž můžeme připočíst ještě jedno makro preprocesoru. Všechny čtyři zmíněné funkce můžeme nalézt v následující tabulce:

# Funkce Význam
1 atom vytvoření reference typu atom
2 deref dereference, vrátí se stav identity
3 reset! atomická změna stavu identity na zvolenou hodnotu
4 swap! atomická změna stavu identity zavoláním zvolené funkce

Atomy se vytváří pomocí funkce atom, které lze předat počáteční (inicializační) hodnotu:

ict ve školství 24

; vytvoření nového atomu
user => (def i (atom 1))
<inst pixie.stdlib.Var>
 
; globální symbol i je navázán
; na atom a nikoli na stav identity
; (=hodnotu)
user => i
<inst pixie.stdlib.Atom>
 
; pro jistotu zkontrolujeme typ objektu
user => (type i)
pixie.stdlib.Atom

Způsob přečtení hodnoty:

; pro získání aktuálního stavu
; je nutné použít dereferenci
user => (deref i)
1
 
; namísto (deref x) se používá
; makro preprocesoru @
user => @i
1
; atomická změna stavu identity
user => (reset! i 42)
42
 
; další možnost atomické změny
; stavu identity - nyní přes funkci
; aplikovanou na atom a popř. i další
; parametry
user => (swap! i inc)
43
 
; samozřejmě lze použít i anonymní funkci
user => (swap! i #(* % %))
1849

12. Atomy a strukturované datové typy

Do atomů se samozřejmě nemusí ukládat jen číselné hodnoty, ale je možné použít i všechny podporované strukturované datové typy neboli kolekce. V dalším příkladu si ukážeme, jak je možné implementovat měnitelnou (mutable) mapu:

; vytvoření prázdné mapy (ta je sama o sobě immutable) a její uložení do atomu
user => (def m (atom {}))
<inst pixie.stdlib.Var>
 
; použití funkce swap! a assoc, funkce assoc vrátí novou mapu s přidaným prvkem
user => (swap! m assoc :first "prvni")
{:first "prvni"}
 
; dereference hodnoty uložené do atomu s využitím makra @
user => @m
{:first "prvni"}
 
; přidání dalšího prvku do mapy
user => (swap! m assoc :second "druhy")
{:first "prvni", :second "druhy"}
 
; přidání třetího prvku do mapy
user => (swap! m assoc :third "treti")
{:first "prvni", :third "treti", :second "druhy"}
 
; odstranění dvou prvků specifikovaných svým klíčem z mapy
user => (swap! m dissoc :first :second)
{:third "treti"}

13. Odkazy na předchozí části tohoto seriálu

  1. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  2. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  3. Clojure 3: Funkcionální programování
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/
  4. Clojure 4: Kolekce, sekvence a lazy sekvence
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/
  5. Clojure 5: Sekvence, lazy sekvence a paralelní programy
    http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/
  6. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  7. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  8. Clojure 8: Identity, stavy, neměnné hodnoty a reference
    http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/
  9. Clojure 9: Validátory, pozorovatelé a kooperace s Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/
  10. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  11. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  12. Clojure 12: Překlad programů z Clojure do bajtkódu JVM I:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/
  13. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  14. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  15. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  16. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  17. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  18. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  19. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  20. Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/
  21. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
  22. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  23. Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  24. Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/
  25. Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/
  26. Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/
  27. Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/
  28. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  29. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  30. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/
  31. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/
  32. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/
  33. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-2/
  34. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  35. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (2)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-2/
  36. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (3)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-3/
  37. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  38. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-dokonceni/
  39. Programovací jazyk Clojure a práce s Gitem (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
  40. Programovací jazyk Clojure – triky při práci s řetězci
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/
  41. Programovací jazyk Clojure – triky při práci s kolekcemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/
  42. Programovací jazyk Clojure – práce s mapami a množinami
    http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/
  43. Programovací jazyk Clojure – základy zpracování XML
    http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/
  44. Programovací jazyk Clojure – testování s využitím knihovny Expectations
    http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/
  45. Programovací jazyk Clojure – některé užitečné triky použitelné (nejenom) v testech
    http://www.root.cz/clanky/programovaci-jazyk-clojure-nektere-uzitecne-triky-pouzitelne-nejenom-v-testech/
  46. Enlive – výkonný šablonovací systém pro jazyk Clojure
    http://www.root.cz/clanky/enlive-vykonny-sablonovaci-system-pro-jazyk-clojure/
  47. Nástroj Leiningen a programovací jazyk Clojure: tvorba vlastních knihoven pro veřejný repositář Clojars
    http://www.root.cz/clanky/nastroj-leiningen-a-programovaci-jazyk-clojure-tvorba-vlastnich-knihoven-pro-verejny-repositar-clojars/
  48. Novinky v Clojure verze 1.8.0
    http://www.root.cz/clanky/novinky-v-clojure-verze-1–8–0/
  49. Asynchronní programování v Clojure s využitím knihovny core.async
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async/
  50. Asynchronní programování v Clojure s využitím knihovny core.async (pokračování)
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-pokracovani/
  51. Asynchronní programování v Clojure s využitím knihovny core.async (dokončení)
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-dokonceni/
  52. Vytváříme IRC bota v programovacím jazyce Clojure
    http://www.root.cz/clanky/vytvarime-irc-bota-v-programovacim-jazyce-clojure/
  53. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  54. Multimetody v Clojure aneb polymorfismus bez použití OOP
    https://www.root.cz/clanky/multimetody-v-clojure-aneb-polymorfismus-bez-pouziti-oop/
  55. Práce s externími Java archivy v programovacím jazyku Clojure
    https://www.root.cz/clanky/prace-s-externimi-java-archivy-v-programovacim-jazyku-clojure/

14. Odkazy na Internetu

  1. Pixie (repositář na GitHubu)
    https://github.com/pixie-lang/pixie
  2. Pixie Installation
    https://github.com/pixie-lang/pixie/wiki/Installation
  3. Pixie Libraries
    https://github.com/pixie-lang/pixie/wiki/Libraries
  4. Interview with Timothy Baldridge, Pixie's language creator
    https://notamonadtutorial.com/indie-languages-interview-pixie-and-timothy-baldridge-cadbc36418dc#.vhbl5rp1c
  5. Pixie (programming language) [Wikipedia]
    https://en.wikipedia.org/wi­ki/Pixie_%28programming_lan­guage%29
  6. Clojure home page
    http://clojure.org/
  7. Clojure (downloads)
    http://clojure.org/downloads
  8. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  9. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  10. 4Clojure
    http://www.4clojure.com/
  11. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  12. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  13. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  14. Clojars:
    https://clojars.org/
  15. Seznam knihoven na Clojars:
    https://clojars.org/projects
  16. Zip archiv s Clojure 1.8.0
    http://repo1.maven.org/ma­ven2/org/clojure/clojure/1­.8.0/clojure-1.8.0.zip
  17. Clojure 1.8 is now available
    http://clojure.org/news/2016/01/19/clo­jure18
  18. Changes to Clojure in Version 1.8
    https://github.com/clojure/clo­jure/blob/master/changes.md
  19. Clojure core.async
    http://www.infoq.com/presen­tations/clojure-core-async
  20. core.async API Reference
    https://clojure.github.io/core.async/
  21. Clojure core.async Channels
    http://clojure.com/blog/2013/06/28/clo­jure-core-async-channels.html
  22. core.async examples
    https://github.com/clojure/co­re.async/blob/master/exam­ples/walkthrough.clj
  23. Timothy Baldridge – Core.Async
    https://www.youtube.com/wat­ch?v=enwIIGzhahw

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.