Obsah
1. Programovací jazyk Janet: varianta Clojure vestavitelná do nativních aplikací
2. Základní vlastnosti programovacího jazyka Janet
3. Překlad interpretru jazyka Janet
5. Pravidla pro vyhodnocování výrazů
12. Lokální symboly ve funkcích
15. Imperativní přístup – použití programových smyček
18. Předchozí části seriálu o LISPovských programovacích jazycích
1. Programovací jazyk Janet: varianta Clojure vestavitelná do nativních aplikací
V seriálu o rozsáhlém a současně i dosti nepřehledném světě LISPovských programovacích jazyků jsme se již několikrát setkali s jazyky odvozenými od známého programovacího jazyka Clojure nebo od Common LISPu (popř. od obou těchto jazyků). Ovšem prozatím jsme si nepopsali jazyk nazvaný Janet (což je relativně nový přírůstek). Tento jazyk je z větší části naprogramován v céčku (zatímco Clojure je naprogramován v Javě, alternativně v C#) a díky tomu jsou jeho systémové nároky poměrně malé. Dosti podstatným způsobem se zrychlila také doba inicializace interpretru, což je oblast, v níž je Clojure velmi špatný (a vznikly proto i projekty, které se snaží tento problém řešit, tedy vlastně klasické „narovnáváky na ohýbák“). A to hlavní na závěr – programovací jazyk Janet je snadno vestavitelný do nativních aplikací.
Obrázek 1: Logo programovacího jazyka Clojure.
Z tohoto pohledu se tedy jedná o programovací jazyk ze stejné kategorie, kam zařazujeme GNU Guile či programovací jazyk Lua. V navazujících kapitolách se seznámíme s některými vlastnosti programovacího jazyka Janet.
Obrázek 2: Logo programovacího jazyka Janet.
2. Základní vlastnosti programovacího jazyka Janet
Programovací jazyk Janet jsme sice v úvodní části dnešního článku zařadili mezi skupinu jazyků inspirovaných jazykem Clojure, to se ovšem týká především syntaxe a sémantiky vlastního jazyka (tedy jak způsobu zápisu výrazů, tak i významu tohoto zápisu). Pokud se ovšem blíže podíváme na způsob práce s hodnotami (values), uvidíme, že se v tomto ohledu jazyk Janet spíše podobá běžným (možná lépe řečeno mainstreamovým) programovacím jazykům. V Clojure jsou totiž hodnoty neměnitelné a navíc se rozlišují tři zcela různě se chovající možnosti změny hodnoty přiřazené k symbolu (včetně podpory transakční paměti) – v tomto programovacím jazyku totiž nenalezneme běžné proměnné. V jazyku Janet je tomu poněkud jinak, protože zde existuje koncept klasických proměnných a rozlišení měnitelné/neměnitelné (muttable/immutable) je dáno datovým typem a nejedná se tedy o vlastnost programovacího jazyka.
Důležitou součástí prakticky jakéhokoli moderního jazyka jsou datové struktury, které je možné přímo začít používat, a to bez nutnosti jejich ruční implementace. V programovacím jazyku Janet nalezneme celkem šest základních datových struktur rozdělených podle jejich vlastností (tři různá rozhraní, měnitelné vs. neměnitelné). Tyto datové struktury jsou vypsány v následující tabulce:
Typ | Měnitelná varianta | Neměnitelná varianta |
---|---|---|
Indexed | Array | Tuple |
Dictionary | Table | Struct |
Bytes | Buffer | String, Symbol, Keyword |
Se způsoby práce s těmito strukturami se seznámíme v praktické části.
A pochopitelně je Janet funkcionálním jazykem v tom smyslu, že funkce jsou plnohodnotnými datovými typy. A pochopitelně jsou podporovány i anonymní funkce, funkce vyššího řádu a uzávěry (closure). K těmto konceptům se ještě vrátíme v dalším textu.
3. Překlad interpretru jazyka Janet
I když je Janet primárně určený (určená?) pro vestavění do dalších aplikací, je možné si nechat přeložit plnohodnotný interpret tohoto jazyka a v něm si vyzkoušet všechny demonstrační příklady, které si v dalším textu ukážeme. Zdrojové kódy Janet jsou dostupné na GitHubu, konkrétně v repositáři na adrese https://github.com/janet-lang/janet. Nejprve si tedy tento repositář naklonujeme:
$ git clone git@github.com:janet-lang/janet.git
Dále provedeme vlastní překlad. K tomuto účelu je nutné mít nainstalován překladač jazyka C (používám GCC), standardní knihovnu céčka, knihovnu pro POSIXové thready a dále pochopitelně linker a taktéž nástroj make. Žádné další závislosti nejsou striktně vyžadovány.
Překlad se spustí takto:
$ make
Samotný překlad interpretru programovacího jazyka Janet je přitom poměrně rychlý a na mém poněkud obstarožním testovacím stroji byl dokončen za jedenáct sekund (včetně spuštění linkeru atd):
real 0m11,425s user 0m10,884s sys 0m0,532s
Výsledný interpret je dostupný v podadresáři build:
$ build/janet
V tomto podadresáři jsou umístěny i další soubory vytvořené při překladu, zejména knihovna, kterou lze slinkovat s dalšími aplikacemi. Velikosti těchto souborů jsou poněkud větší, než je tomu například u jazyka Lua, jenž je v tomto ohledu pojat minimalisticky:
-rwxrwxr-x 1 ptisnovs ptisnovs 2,3M Feb 12 14:36 janet -rwxrwxr-x 1 ptisnovs ptisnovs 1,5M Feb 12 14:36 janet_boot -rw-rw-r-- 1 ptisnovs ptisnovs 4,6M Feb 12 14:36 libjanet.a -rwxrwxr-x 1 ptisnovs ptisnovs 2,3M Feb 12 14:36 libjanet.so
Mimochodem, zkontrolujme pro jistotu, na jakých dalších knihovnách interpret závisí:
$ ldd build/janet linux-vdso.so.1 (0x00007ffc27e99000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f719ad5d000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f719ad3a000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f719ad34000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f719ab42000) /lib64/ld-linux-x86-64.so.2 (0x00007f719af87000)
4. Interaktivní smyčka (REPL)
Interaktivní smyčku REPL (Read-Eval-Print Loop) spustíme příkazem build/janet. Na prvním řádku by se měla vypsat verze programovacího jazyka a ihned na řádku druhém takzvaná výzva (prompt). Každá výzva je očíslována, jak ostatně uvidíme dále:
Janet 1.33.0-local linux/x64/gcc - '(doc)' for help repl:1:>
Jakýkoli zapsaný výraz (a prakticky vše je v jazyku Janet výrazem) se zvaliduje a vyhodnotí. Výsledek se vypíše na novém řádku (nebo na více řádcích):
repl:1:> (* 6 7) 42
Důležité je zobrazení nápovědy. Seznam všech aktuálně viditelných symbolů se zobrazí po zavolání makra doc bez parametrů:
repl:6:> (doc)
Seznam symbolů je příliš dlouhý, uvedu zde tedy jen zkrácený seznam:
Bindings: % %= * *= *args* *current-file* *debug* *defdyn-prefix* *doc-color* *doc-width* *err* *err-color* *executable* *exit* *exit-value* *ffi-context* *lint-error* *lint-levels* *lint-warn* *macro-form* *macro-lints* *out* *peg-grammar* *pretty-format* *profilepath* *redef* *syspath* *task-id* + ++ += - -- -= -> ->> -?> -?>> / /= < <= = > >= _ abstract? accumulate accumulate2 all all-bindings all-dynamics and any? apply array array/clear array/concat array/ensure array/fill array/insert array/new array/new-filled array/peek array/pop array/push array/remove array/slice array/trim array/weak array? as-> as-macro as?-> asm assert bad-compile bad-parse band blshift bnot boolean? bor brshift brushift buffer buffer/bit buffer/bit-clear buffer/bit-set buffer/bit-toggle ... ... ...
Nápovědu pro konkrétní symbol lze zobrazit opět zavoláním makra doc, tentokrát ovšem s uvedením symbolu, jehož nápovědu potřebujeme znát:
repl:11:> (doc tuple) cfunction src/core/corelib.c on line 397, column 1 (tuple & items) Creates a new tuple that contains items. Returns the new tuple.
Nápověda existuje i pro funkce s „divnými“ jmény:
repl:12:> (doc +) function (+ & xs) Returns the sum of all xs. xs must be integers or real numbers only. If xs is empty, return 0.
A konečně pro opuštění interaktivní smyčky REPL slouží funkce quit:
repl:1:> (quit) nil
5. Pravidla pro vyhodnocování výrazů
V této kapitole si ve stručnosti popíšeme způsob zápisu programů a vyhodnocování jednotlivých výrazů. Základem interpretace každého programu zapsaného v jazyku Janet je načtení a rozpoznání jednoho výrazu (read), vyhodnocení tohoto výrazu (eval) a následný tisk výsledné hodnoty výrazu na standardní výstup (print). Pravidla pro vyhodnocení výrazů (někdy se též můžeme setkat s termínem „vyhodnocení forem“) jsou v tomto jazyku, stejně jako v ostatních LISPovských jazycích, ve skutečnosti velmi jednoduchá a přímočará, na rozdíl od mnoha jiných programovacích jazyků (vlastně na rozdíl od všech mainstreamových jazyků). Tato pravidla lze ve zjednodušené podobě sepsat do pouhých několika bodů:
- čísla, hodnoty true a false, hodnota nil, řetězce a klíčová slova (keywords) jsou literály, které jsou vyhodnoceny samy na sebe (což je logické – jedná se o dále nedělitelné objekty, tedy o atomy)
- hodnotou symbolu je objekt, který je na tento symbol navázán (analogie z jiných programovacích jazyků – hodnotou proměnné zapsané svým jménem je hodnota uložená do proměnné)
- seznamy jsou vyhodnocovány tak, že se první prvek seznamu chápe jako jméno funkce (či jako jméno takzvané speciální formy), které je předán zbytek seznamu jako parametry této funkce (nebo speciální formy)
- v případě, že seznam obsahuje podseznamy, jsou tyto podseznamy vyhodnoceny nejdříve, přičemž úroveň rekurzivního zanořování při vyhodnocování podseznamů není teoreticky omezena (tj. podseznamy lze vnořovat do téměř libovolné úrovně)
- makra jsou expandována a výsledek této expanze je rekurzivně vyhodnocen
- ostatní hodnoty (pole apod.) jsou vyhodnoceny samy na sebe, což například znamená, že pole vrátí svoji hodnotu atd.
Příklady vyhodnocení různých výrazů přímo v REPLu jazyka Janet:
repl:1:> "Hello, world!" "Hello, world!" repl:2:> :toto-je-keyword :toto-je-keyword repl:3:> 'toto-je-symbol toto-je-symbol repl:4:> true true repl:5:> false false repl:6:> nil nil repl:7:> (+ 1 2) 3 repl:8:> [1 2 3 4] (1 2 3 4) repl:9:> @[1 2 3 4] @[1 2 3 4] repl:10:> (1 2 3 4) repl:10:1: compile error: 1 expects 1 argument, got 3
6. Klíčová slova
Dalším typem literálu podporovaného programovacím jazykem Janet jsou takzvané symboly známé už z LISPu, ke kterým se v jazyce Janet ještě přidávají keywords. Začněme s popisem „keywords“. Překlad tohoto slova je poněkud problematický kvůli jeho dvojímu významu (alespoň v programování), takže se pokusím používat sousloví „klíčová hesla“, protože termín „keywords“ v jazyce Janet neznamená, že by se jednalo o rezervovaná klíčová slova jazyka. Klíčová hesla jsou na použití jednodušší než symboly, protože se ve smyčce REPL vyhodnocují samy na sebe a nemůže jim být přiřazena žádná hodnota. Na co se tedy vlastně v praxi tento typ formy hodí? Jedním z důvodů zavedení tohoto typu formy do programovacího jazyka Janet byla podpora pro datového typu (kolekce) struct (tedy mapy), v níž je možné uchovávat dvojice klíč:hodnota. A jako klíč jsou s výhodou používána právě klíčová hesla, protože jejich hodnotu nelze měnit a navíc se jejich hešovací hodnota může vypočítat pouze jedenkrát. Smyčka REPL pozná, že uživatel používá klíčové heslo z toho, že je těsně před ním napsána dvojtečka. Jak již bylo řečeno výše, vyhodnotí se heslo na sebe samu:
repl:1:> :foo :foo repl:2:> :toto-je-taktez-keyword :toto-je-taktez-keyword repl:3:> :i.toto.je.keyword :i.toto.je.keyword
Ve skutečnosti je dvojtečka uváděná před klíčovým heslem jen syntaktickým cukrem, protože „plný“ zápis (v tomto případě speciální) formy představující klíčové heslo vypadá takto:
repl:5:> (keyword 'foo) :foo repl:6:> (keyword 'toto-je-taktez-keyword) :toto-je-taktez-keyword
Klíčová slova lze porovnávat. Stejně napsané klíčové slovo je vždy totožné s jiným stejně napsaným slovem (to ovšem nemusí platit u všech datových typů):
repl:7:> (= :foo :bar) false repl:8:> (= :foo :foo) true
7. Symboly
Podobným typem formy jsou symboly, před jejichž jménem se používá ampersand. Symbolům může být přiřazena hodnota a z tohoto důvodu se používají pro pojmenování funkcí, proměnných či jmenných prostorů. Se symboly se ještě v tomto článku několikrát setkáme, nyní si tedy jen ukažme, jak se symboly zapisují a jak je smyčka REPL vyhodnotí:
repl:1:> 'symbol symbol repl:2:> 'i-toto-je-symbol i-toto-je-symbol repl:3:> 'toto.je.taktez.symbol toto.je.taktez.symbol
Podobně jako dvojtečka byla pouze syntaktickým cukrem pro speciální formu (keyword „xxx“), je i ampersand zkrácenou podobou speciální formy (quote symbol). Předchozí příklad by tedy šel zapsat i následujícím způsobem:
repl:4:> (quote symbol) symbol repl:5:> (quote i-toto-je-symbol) i-toto-je-symbol repl:8:> (quote toto.je.taktez.symbol) toto.je.taktez.symbol
To, že se v obou případech jedná skutečně o stejný symbol, lze zjistit s využitím funkce ekvivalence:
repl:9:> (= 'toto-je-symbol (quote toto-je-symbol)) true
8. Neměnitelné kolekce
Velmi důležitá je podpora pro práci s kolekcemi. Mezi ty patří n-tice (vektory) a struktury. N-tice se v jazyce Clojure nazývá vektor a má poněkud odlišné vlastnosti (má totiž odlišnou interní strukturu, která například umožňuje sdílení struktury). Datový typ struktura zhruba – svými základními vlastnostmi – odpovídá mapě (slovníku) v Clojure, ovšem opět platí, že interně jsou tyto hodnoty reprezentovány odlišným způsobem. Povšimněte si taktéž, že v jazyce Janet je za n-tici považován i typ seznam. Z tohoto pohledu je tedy Janet LISPovským jazykem, ovšem bez seznamů (LIS v názvu LISP značí list):
# | Typ kolekce | Zápis (syntaktický cukr) | Konstruktor |
---|---|---|---|
1 | n-tice (vektor) | [prvky] či '(prvky) | (tuple prvky) |
2 | struktura | {dvojice klíč-hodnota} | (struct) |
Zápis n-tice:
repl:1:> '(1 2 3 4) (1 2 3 4) repl:2:> [1 2 3 4] (1 2 3 4) repl:3:> [1 [2 3] 4] (1 (2 3) 4) repl:4:> [1 [2 [3 [4 5 6]]]] (1 (2 (3 (4 5 6)))) repl:5:> (tuple 1 2 3 4) (1 2 3 4)
Zápis struktury:
repl:1:> {"name" "Pavel" "surname" "Tisnovsky"} {"name" "Pavel" "surname" "Tisnovsky"} repl:2:> {:name "Pavel" :surname "Tisnovsky"} {:name "Pavel" :surname "Tisnovsky"} repl:3:> (struct :name "Pavel" :surname "Tisnovsky") {:name "Pavel" :surname "Tisnovsky"}
Kombinace struktury a n-tice:
repl:10:> {:name "Pavel" :grades [:A :C :F :F :B]} {:grades (:A :C :F :F :B) :name "Pavel"}
Opačná kombinace n-tice a struktury:
repl:11:> [{:name "Pavel" :id 42} {:name "Petr" :id nil}] ({:id 42 :name "Pavel"} {:name "Petr"})
9. Speciální forma def
Důležitá je i speciální forma nazvaná def:
repl:7:> (doc def) special form (def ...) See https://janet-lang.org/docs/specials.html
Tato speciální forma se většinou používá k navázání libovolné hodnoty (například čísla, pravdivostní hodnoty, řetězce, struktury a jak uvidíme dále, tak i funkce) na nějaký symbol. Méně časté je použití této speciální formy k pouhému vytvoření symbolu. V případě, že interpret jazyka Janet vyhodnotí („spustí“) tuto speciální formu, dojde k vytvoření nové globální proměnné v aktuálně nastaveném jmenném prostoru. Pokud již globální proměnná stejného jména existuje, dojde k „přepisu“ její hodnoty. Ve skutečnosti však stará hodnota nemusí přestat existovat, protože může být navázána na další proměnné:
repl:1:> (def x true) true repl:2:> (def y "foo") "foo" repl:3:> (def z :keyword) :keyword repl:4:> (def w (+ 1 2)) 3 repl:5:> (def x false) false
K symbolům lze přiřadit i metadata:
repl:10:> (def a :answer 42) 42
Metadat může být i větší množství:
repl:11:> (def a :answer :todo :need-tests 42) 42
Přiřadit lze i hodnoty k více symbolům. Například takto:
repl:12:> (def [a b c] [10 20 30]) (10 20 30) repl:13:> a 10 repl:14:> b 20 repl:15:> c 30
10. Anonymní funkce
Pro vytvoření nové bezejmenné (tj. anonymní) funkce se v jazyku Janet (ale i v Clojure) používá speciální forma nazvaná fn:
repl:8:> (doc fn) special form (fn ...) See https://janet-lang.org/docs/specials.html
Této speciální formě se v tom nejjednodušším případě předá vektor obsahující jména parametrů, za nímž je uveden seznam, který představuje tělo funkce (znalci LISPu patrně znají formu lambda, která má podobný význam. Samozřejmě, že v těle funkce je možné použít symbolická jména jejích parametrů a návratovou hodnotou funkce je hodnota získaná vyhodnocením těla funkce. Speciální forma fn při použití ve smyčce REPL vypíše řetězec, který reprezentuje interní identifikátor funkce – jinými slovy na tento řetězec můžeme v naprosté většině případů zapomenout, protože se s ním přímo nepracuje. Ukažme si tedy způsob deklarace funkce se dvěma parametry pojmenovanými x a y, která vypočítá a vrátí součet těchto parametrů:
; anonymní funkce (fn [x y] (+ x y)) <function 0x556614507790>
Co se vlastně stalo? Vytvořili jsme novou funkci, která však nebyla přiřazena k žádnému symbolu (tj. nebyla „pojmenována“) ani jsme tuto funkci nikde nezavolali. Výše uvedený zápis je tedy prakticky stejně užitečný, jako prosté zapsání jakékoli hodnoty nebo symbolu na vstup smyčky REPL. Pokud by se funkce měla zavolat, lze použít nám již známý zápis ve tvaru seznamu – již víme, že prvním parametrem vyhodnocovaného seznamu (není před ním apostrof!) je funkce a dalšími prvky pak parametry této funkce:
((fn [x y] (* x y)) 6 7) 42
Sice je pěkné, že jsme dokázali funkci zavolat s předáním parametrů, ovšem mnohdy (ne vždy!) je nutné funkci „pojmenovat“, přesněji řečeno ji přiřadit k symbolu. My vlastně již víme, jak se to dělá, protože funkce jsou hodnotami a pro přiřazení symbolu k hodnotě se používá speciální forma setv. Tudíž následující zápis je sice zdlouhavý, ale zcela korektní:
repl:4:> (def add (fn [x y] (+ x y))) <function 0x556614509A10>
Předchozím příkazem jsme vytvořili novou funkci a navázali ji na symbol, tudíž došlo k jejímu pojmenování. Nyní je již možné funkci zavolat s využitím navázaného symbolu. Samozřejmě se zde opět využívá nám již známý zápis ve tvaru seznamu:
repl:5:> (add 10 20) 30
11. Pojmenované funkce
Vzhledem k tomu, že se uživatelské funkce v reálných programech vytváří a současně i pojmenovávají velmi často, vznikla potřeba nahradit zápis (def název (fn parametry (tělo))) něčím kratším, ideálně i s použitím menšího množství závorek. Pro tyto účely vzniklo makro se jménem defn, které se až na malé detaily podobá LISPovskému zápisu defun:
repl:9:> (doc defn) macro boot.janet on line 10, column 1 (defn name & more) Define a function. Equivalent to (def name (fn name [args] ...)).
Při použití makra defn se v tom nejjednodušším případě předávají tři parametry: název nově vytvářené funkce, vektor obsahující jména parametrů funkce a konečně seznam představující tělo této funkce. Naši funkci multiply tedy můžeme vytvořit a současně i pojmenovat následujícím způsobem:
repl:10:> (defn multiply [x y] (* x y))
A ihned ji můžeme použít:
repl:11:> (multiply 6 7) 42
12. Lokální symboly ve funkcích
Ve funkcích se často používají lokální symboly, resp. lokální proměnné s mezivýsledky výpočtů atd. Ty lze vytvářet makrem let. Prvním parametrem tohoto makra je n-tice obsahující dvojice jméno_symbolu výraz. Do symbolu bude dosazen vypočtený výraz. A dalšími parametry makra je programový blok s jedním či více výrazy. Výsledek posledního výrazu je současně i výsledkem celého aplikovaného makra:
repl:1:> (doc let) macro boot.janet on line 221, column 1 (let bindings & body) Create a scope and bind values to symbols. Each pair in bindings is assigned as if with def, and the body of the let form returns the last value.
Příklad s konstantními výrazy:
(defn foo [] (let [x 1 y 2] (+ x y))) repl:4:> (foo) 3
Ovšem čitelnější zápis funkce foo by mohl vypadat takto:
(defn foo [] (let [x 1 y 2] (+ x y)))
Následuje praktičtější příklad, který sečte absolutní hodnoty argumentů funkce add-abs:
(defn add-abs [x y] (let [abs-x (if (< x 0) (- x) x) abs-y (if (< y 0) (- y) y)] (+ abs-x abs-y)))
Příklady použití:
repl:22:> (add-abs 10 20) 30 repl:23:> (add-abs 10 -20) 30 repl:24:> (add-abs -10 -20) 30
13. Uzávěry
Podpora lokálních symbolů a hodnot, které může funkce vracet, má ovšem dalekosáhlejší důsledky, které mj. ovlivňují činnost správce paměti atd. Jedná se o to, že pokud je nějaká proměnná (která je definovaná vně funkce) na funkci navázána (prakticky: je ve funkci použita), nemůže hodnota této proměnné jednoduše zaniknout ani při opuštění daného bloku, protože společně s funkcí tvoří takzvaný uzávěr (closure). S uzávěry se v LISPovské rodině jazyků setkáme velmi často a dnes je nalezneme i v některých dalších programovacích jazycích (zdaleka ne ve všech).
Příkladem může být „konstruktor funkcí“ mul_prep, který na základě jediného předaného parametru vytvoří a vrátí funkci, která bude (při svém pozdějším volání) násobit svůj argument tímto parametrem:
(defn mul_prep [x] (fn [y] (* x y))) ; toto je návratová hodnota typu funkce
Necháme si tímto konstruktorem vytvořit funkci pro vynásobení jediného argumentu dvojkou:
(def doubler (mul_prep 2))
A použijeme ji:
repl:18:> (doubler 1) 2
Podobně lze vytvořit konstruktor funkcí pro porovnání předané hodnoty s předem nastaveným limitem:
(defn larger-than [limit] (fn (value) (> value limit))) ; toto je návratová hodnota typu funkce
Příklad použití:
repl:3:> (def larger-than-5 (larger-than 5)) <function 0x55615507E860> repl:4:> (larger-than-5 10) true repl:5:> (larger-than-5 2) false repl:6:> (larger-than-5 4) false repl:7:> (filter larger-than-5 '(1 2 3 4 5 6 7 8 9 10)) @[6 7 8 9 10]
14. Rekurze a její limity
Ve funkcionálních programovacích jazycích se velmi často setkáme s algoritmy založenými na rekurzi a na tail rekurzi. Zcela typickým příkladem rekurzivní funkce je funkce pro výpočet faktoriálu, jejíž jednoduchá varianta (neochráněná před všemi typy vstupů) může vypadat takto:
; rekurzivní výpočet faktoriálu (defn factorial [n] (if (<= n 1) 1 (* n (factorial (- n 1)))))
Otestování takto definované je snadné:
(factorial 10) 3628800
repl:22:> (factorial 1000) inf
Pro ještě větší hodnoty n ovšem dojde k následující chybě:
repl:25:> (factorial 1000000000) src/core/fiber.c:122 - janet out of memory
Důvod, proč předchozí volání funkce factorial skončilo s chybou, spočívá v tom, že došlo k přeplnění zásobníku při rekurzivním volání. Na zásobník se totiž musí ukládat parametry předávané volané funkci a taktéž body návratu (zjednodušeně řečeno návratové adresy). Tento problém se řeší buď tail rekurzí (optimalizace jazyka) nebo explicitním zápisem programové smyčky, což sice není příliš funkcionální přístup, ale jedná se o technologii, kterou programovací jazyk Janet nabízí.
15. Imperativní přístup – použití programových smyček
Na rozdíl od programovacího jazyka Clojure je možné v jazyce Janet použít i běžný (mainstreamový) imperativní přístup. Týká se to mj. i podpory programových smyček. Základem je přitom programová smyčka typu while, která je realizována jako speciální forma:
repl:87:> (doc while) special form (while ...) See https://janet-lang.org/docs/specials.html
Podívejme se nyní na to, jakým způsobem lze přepsat výpočet faktoriálu z funkcionální podoby založené na přímé rekurzi do imperativní podoby založené na programové smyčce while a proměnných vytvářených speciální formou var. Proměnné deklarované přes var jsou měnitelné (immutable). Povšimněte si též funkce ++, která dokáže zvýšit hodnotu proměnné, podobně jako stejně pojmenovaný operátor známý z jiných programovacích jazyků:
(defn factorial [n] (var i 1) (var result 1) (while (<= i n) (*= result i) (++ i)) result)
Pro programové smyčky s počitadlem je navíc možné namísto speciální formy while (která je pro tyto účely příliš primitivní) použít makro for, které je do značné míry podobné příkazu for z jazyků odvozených od céčka:
repl:88:> (doc for) macro boot.janet on line 503, column 1 (for i start stop & body) Do a C-style for-loop for side effects. Returns nil.
Přepis výpočtu faktoriálu tak, aby se využilo makro for, může vypadat takto:
(defn factorial [n] (var result 1) (for i 1 (+ n 1) (*= result i)) result)
repl:13:> (doc repeat) macro boot.janet on line 518, column 1 (repeat n & body) Evaluate body n times. If n is negative, body will be evaluated 0 times. Evaluates to nil.
16. Makro loop
V programovacím jazyku Janet nalezneme i velmi mocné (a interně komplikované) makro nazvané loop, které je do značné míry převzaté z Common LISPu. Toto makro programátorům nabízí svůj vlastní doménově specifický jazyk (DSL). Z dalších demonstračních příkladů bude patrné, že tento jazyk používá styl zápisu, který je kombinací klasických strukturovaných jazyků (Algol, Pascal, C) a možností LISPu. Je tomu tak z toho důvodu, aby bylo přímo ze zápisu smyčky, typicky již po přečtení prvního řádku, patrné, jak bude smyčka prováděna. K tomuto účelu se uvnitř smyčky loop používají symboly for, repeat, in, finally atd., které mají svůj speciální význam, ale pouze uvnitř samotné formy loop. Tím se loop do značné míry liší od smyček v klasických jazycích, které programátory „nutí“ realizovat kód smyčky v jejím těle, což nemusí být vždy idiomatické:
repl:89:> (doc loop) macro boot.janet on line 534, column 1 (loop head & body) A general purpose loop macro. This macro is similar to the Common Lisp loop macro, although intentionally much smaller in scope. The head of the loop should be a tuple that contains a sequence of either bindings or conditionals. A binding is a sequence of three values that define something to loop over. Bindings are written in the format:
V dnešním článku si ukážeme jen základní způsob použití tohoto makra, a to opět při výpočtu faktoriálu. Předchozí příklady lze transformovat do této podoby:
(defn factorial [n] (var result 1) (loop [i :range [1 (+ n 1)]] (*= result i)) result)
Ještě je vhodné nahradit výraz (+ n 1) za (inc n), což je v LISPovských jazycích idiomatičtější:
(defn factorial [n] (var result 1) (loop [i :range [1 (inc n)]] (*= result i)) result)
Navíc je možné použít smyčku počítající až do horní meze (včetně), což celý zápis opět zkrátí a zpřehlední:
(defn factorial [n] (var result 1) (loop [i :range-to [1 n]] (*= result i)) result)
17. Další použití makra loop
Podívejme se pro úplnost na některé další možnosti použití makra loop. Můžeme například přidat podmínku pro vykonání těla smyčky (tedy v jiných jazycích by se jednalo o kombinaci smyčky for s podmíněným blokem if):
(var total 0) (loop [i :range [0 10] :when (< i 5)] (+= total i)) (print total)
Tato smyčka by měla vypsat hodnotu 10, protože se sečetla čísla 0 a až 4 (včetně).
Otočení podmínky záměnou slova :when za :unless:
(var total 0) (loop [i :range [0 10] :unless (< i 5)] (+= total i)) (print total)
Ukončení smyčky při nesplnění podmínky:
(var total 0) (loop [i :range [0 10] :while (< i 5)] (+= total i)) (print total)
A naopak ukončení smyčky při splnění podmínky:
(var total 0) (loop [i :range [0 10] :until (< i 5)] (+= total i)) (print total)
Počítání směrem dolů:
repl:5:> (loop [i :down [10 0]] (print i)) 10 9 8 7 6 5 4 3 2 1 nil
Taktéž počítání směrem dolů, ovšem nyní včetně dolní mezní hodnoty:
repl:7:> (loop [i :down-to [10 0]] (print i)) 10 9 8 7 6 5 4 3 2 1 0
Iterace přes všechny prvky n-tice:
(loop [i :in [1 2 3 4 10]] (print i))
Mělo by se vypsat:
1 2 3 4 10
Což je ovšem možné zkrátit využitím makra each:
repl:24:> (doc each) macro boot.janet on line 529, column 1 (each x ds & body) Loop over each value in ds. Returns nil.
Zkrácený zápis bude vypadat následovně:
(each i [1 2 3 4 10] (print i))
Iterace přes prvky (tedy dvojice klíč+hodnota) uložené ve struktuře:
repl:31:> (def x {"A" 1 "B" 2 "C" 3}) {"A" 1 "B" 2 "C" 3} repl:32:> (loop [[key value] :pairs x] (print key "->" value)) C->3 A->1 B->2 nil
Průchod klíči uloženými ve struktuře:
18. Předchozí části seriálu o LISPovských programovacích jazycích
V této kapitole jsou uvedeny odkazy na všechny předchozí části seriálu o světě programovacích jazyků LISP a Scheme (kromě samostatného seriálu, který se věnoval programovacímu jazyku Clojure):
- Jemný úvod do rozsáhlého světa jazyků LISP a Scheme
https://www.root.cz/clanky/jemny-uvod-do-rozsahleho-sveta-jazyku-lisp-a-scheme/ - PicoLisp: minimalistický a přitom překvapivě výkonný interpret Lispu
https://www.root.cz/clanky/picolisp-minimalisticky-a-pritom-prekvapive-vykonny-interpret-lispu/ - PicoLisp: užitečné funkce a speciální formy používané při tvorbě aplikací
https://www.root.cz/clanky/picolisp-uzitecne-funkce-a-specialni-formy-pouzivane-pri-tvorbe-aplikaci/ - PicoLisp: dokončení popisu a několik praktických rad na závěr
https://www.root.cz/clanky/picolisp-dokonceni-popisu-a-nekolik-praktickych-rad-na-zaver/ - GNU Guile – interpret Scheme vestavitelný do nativních aplikací
https://www.root.cz/clanky/gnu-guile-interpret-scheme-vestavitelny-do-nativnich-aplikaci/ - TinyScheme aneb další interpret jazyka Scheme vestavitelný do dalších aplikací
https://www.root.cz/clanky/tinyscheme-aneb-dalsi-interpret-jazyka-scheme-vestavitelny-do-dalsich-aplikaci/ - Kawa: překvapivě silný a výkonný dialekt Scheme pro JVM
https://www.root.cz/clanky/kawa-prekvapive-silny-a-vykonny-dialekt-scheme-pro-jvm/ - Jazyk Kawa v ekosystému virtuálního stroje Javy
https://www.root.cz/clanky/jazyk-kawa-v-ekosystemu-virtualniho-stroje-javy/ - Zpracování vektorů, matic a N-rozměrných polí v programovacím jazyku Kawa
https://www.root.cz/clanky/zpracovani-vektoru-matic-a-n-rozmernych-poli-v-programovacim-jazyku-kawa/ - Racket: programovací jazyk a současně i platforma pro vývoj nových jazyků
https://www.root.cz/clanky/racket-programovaci-jazyk-a-soucasne-i-platforma-pro-vyvoj-novych-jazyku/ - Makra v Racketu i v dalších lispovských jazycích
https://www.root.cz/clanky/makra-v-racketu-i-v-dalsich-lispovskych-jazycich/ - Základní knihovna jazyka Racket
https://www.root.cz/clanky/zakladni-knihovna-jazyka-racket/ - Jazyk Joker: dialekt Clojure naprogramovaný v Go
https://www.root.cz/clanky/jazyk-joker-dialekt-clojure-naprogramovany-v-go/ - Chicken Scheme – další interpret a především překladač programovacího jazyka Scheme
https://www.root.cz/clanky/chicken-scheme-dalsi-interpret-a-predevsim-prekladac-programovaciho-jazyka-scheme/ - Projekt Gambit – další kvalitní interpret i překladač programovacího jazyka Scheme
https://www.root.cz/clanky/projekt-gambit-dalsi-kvalitni-interpret-i-prekladac-programovaciho-jazyka-scheme/ - Interlisp aneb oživujeme dinosaura
https://www.root.cz/clanky/interlisp-aneb-ozivujeme-dinosaura/ - Propojení světa LISPu se světem JavaScriptu s využitím transpřekladače Wisp
https://www.root.cz/clanky/propojeni-sveta-lispu-se-svetem-javascriptu-s-vyuzitim-transprekladace-wisp/ - Propojení světa LISPu se světem JavaScriptu s využitím transpřekladače Wisp (2.část)
https://www.root.cz/clanky/propojeni-sveta-lispu-se-svetem-javascriptu-s-vyuzitim-transprekladace-wisp-2-cast/ - Common Lisp: žralok mezi programovacími jazyky
https://www.root.cz/clanky/common-lisp-zralok-mezi-programovacimi-jazyky/ - Common Lisp: žralok mezi programovacími jazyky (2.část)
https://www.root.cz/clanky/common-lisp-zralok-mezi-programovacimi-jazyky-2-cast/
Články o Elispu:
- Úpravy Emacsu a tvorba nových modulů s využitím Emacs Lispu
https://www.root.cz/clanky/upravy-emacsu-a-tvorba-novych-modulu-s-vyuzitim-emacs-lispu/ - Úpravy Emacsu s Emacs Lisp: základní konstrukce jazyka
https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-zakladni-konstrukce-jazyka/ - Úpravy Emacsu s Emacs Lisp: všemocné makro cl-loop a knihovna dash
https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-vsemocne-makro-cl-loop-a-knihovna-dash/ - Úpravy Emacsu s Emacs Lisp: možnosti nabízené knihovnou Dash
https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-moznosti-nabizene-knihovnou-dash/ - Úpravy Emacsu s Emacs Lisp: dokončení popisu Emacs Lispu
https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-dokonceni-popisu-emacs-lispu/ - Úpravy Emacsu s Emacs Lisp: manipulace se základními datovými strukturami Emacsu
https://www.root.cz/clanky/upravy-emacsu-s-emacs-lisp-manipulace-se-zakladnimi-datovymi-strukturami-emacsu/
19. Literatura
O Common Lispu, Scheme či Clojure, tedy o třech (s velkou pravděpodobností) nejpoužívanějších dialektech LISPu, vyšlo poměrně velké množství literatury. Pro Common Lisp je typická jeho velká stabilita, a to minimálně od roku 1994, což mj. znamená, že i původní vydaní prvních dvou dále zmíněných knih je zcela bez problémů použitelné i dnes (a obě knihy jsou navíc dobře čitelné):
- Peter Seibel
„Practical Common Lisp“
2009 - Paul Graham
„ANSI Common Lisp“
1995 - Gerald Gazdar
„Natural Language Processing in Lisp: An Introduction to Computational Linguistics“
1989 - Peter Norvig
„Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp“
1991 - Alex Mileler et.al.
„Clojure Applied: From Practice to Practitioner“
2015 - „Living Clojure: An Introduction and Training Plan for Developers“
2015 - Dmitri Sotnikov
„Web Development with Clojure: Build Bulletproof Web Apps with Less Code“
2016 - McCarthy
„Recursive functions of symbolic expressions and their computation by machine, part I“
1960 - R. Kent Dybvig
„The Scheme Programming Language“
2009 - Max Hailperin
„Concrete Abstractions“
1998 - Guy L. Steele
„History of Scheme“
2006, Sun Microsystems Laboratories - Kolář J., Muller K.:
„Speciální programovací jazyky“
Praha 1981 - „AutoLISP Release 9, Programmer's reference“
Autodesk Ltd., October 1987 - „AutoLISP Release 10, Programmer's reference“
Autodesk Ltd., September 1988 - McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I.
„LISP 1.5 Programmer's Manual“
MIT Press. ISBN 0 262 130 1 1 4 - Carl Hewitt; Peter Bishop and Richard Steiger
„A Universal Modular Actor Formalism for Artificial Intelligence“
1973 - Feiman, J.
„The Gartner Programming Language Survey (October 2001)“
Gartner Advisory - Harold Abelson, Gerald Jay Sussman, Julie Sussman:
Structure and Interpretation of Computer Programs
MIT Press. 1985, 1996 (a možná vyšel i další přetisk) - Paul Graham
On Lisp
Prentice Hall, 1993
Dostupné online na adrese http://www.paulgraham.com/onlisptext.html - David S. Touretzky
Common LISP: A Gentle Introduction to Symbolic Computation (Dover Books on Engineering)
- Peter Norvig
Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp - Patrick Winston, Berthold Horn
Lisp (3rd Edition)
ISBN-13: 978–0201083194, ISBN-10: 0201083191 - Matthias Felleisen, David Van Horn, Dr. Conrad Barski
Realm of Racket: Learn to Program, One Game at a Time!
ISBN-13: 978–1593274917, ISBN-10: 1593274912
20. Odkazy na Internetu
- Janet Language
https://janet-lang.org/ - Janet na GitHubu
https://github.com/janet-lang/janet - Janet for Mortals (a real book)
https://janet.guide/ - Bauble studio
https://bauble.studio/ - Janet na Hacker News
https://news.ycombinator.com/item?id=34843306 - Common Lisp
https://lisp-lang.org/ - Why You Should Learn Lisp In 2022?
https://www.youtube.com/watch?v=GWdf1flcLoM - LOOP Common Lisps Superior For
https://www.youtube.com/watch?v=i4tmF_1nZng - Lisp VS C benchmarks
https://programming-language-benchmarks.vercel.app/lisp-vs-c - Common Lisp: An elegant design pattern
https://www.youtube.com/watch?v=9597LFlvMuE - Common Lisp Macros By Example Tutorial
https://lisp-journey.gitlab.io/blog/common-lisp-macros-by-example-tutorial/ - The Common Lisp Cookbook
https://lispcookbook.github.io/cl-cookbook/ - The Evolution of Lisp
https://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-Lisp.pdf - Awesome CL
https://github.com/CodyReichert/awesome-cl - LISP
https://taoofmac.com/space/dev/lisp - Repositář projektu femtolisp
https://github.com/JeffBezanson/femtolisp - Femtolisp – lightweight, robust lisp interpreter built on reusable C libraries
https://www.findbestopensource.com/product/femtolisp - YCombinator: Femtolisp: A lightweight, robust, scheme-like Lisp implementation
https://news.ycombinator.com/item?id=22094722 - Learning Julia by Anshul Joshi, Rahul Lakhanpal: Femtolisp
https://www.oreilly.com/library/view/learning-julia/9781785883279/2e85442f-d100–4b53-b8f7–7d20d62f0255.xhtml - The role of femtolisp in Julia?
https://discourse.julialang.org/t/the-role-of-femtolisp-in-julia/1902 - LispSyntax.jl: A clojure-like lisp syntax for julia
https://github.com/swadey/LispSyntax.jl - What exactly code lowering is an how to do “unlowering”?
https://discourse.julialang.org/t/what-exactly-code-lowering-is-an-how-to-do-unlowering/1315 - Interlisp.org: Dedicated to Restoring and Preserving the Interlisp experience
https://github.com/Interlisp - Warren Teitelman
https://en.wikipedia.org/wiki/Warren_Teitelman - InterLISP/65
http://www.atarimania.com/utility-atari-400–800-xl-xe-interlisp-65_12477.html - Lisp Editing in the 80s – Interlisp SEdit (Video)
https://www.youtube.com/watch?v=2qsmF8HHskg - Inter-LISP
http://www.atarimania.com/utility-atari-400–800-xl-xe-inter-lisp_29354.html - InterLISP 65 Editing (video)
https://www.youtube.com/watch?v=nY_hcazo86A - Datasoft INTER-LISP/65 (Atari Age, chat)
https://atariage.com/forums/topic/116093-datasoft-inter-lisp65/ - Marvin Minsky – The beauty of the Lisp language (44/151)
https://www.youtube.com/watch?v=YaWVHyIBVeI - History of LISP (Interlisp)
http://www.softwarepreservation.org/projects/LISP/index.html#INTERLISP_ - Computer-Assisted Instruction (Bits and Bytes, Episode 7)
https://www.youtube.com/watch?v=eURtTV_qKw8 - Můžeme věřit překladačům? Projekty řešící schéma „důvěřivé důvěry“
https://www.root.cz/clanky/muzeme-verit-prekladacum-projekty-resici-schema-duverive-duvery/ - Gambit in the browser
https://feeley.github.io/gambit-in-the-browser/ - A Tour of Scheme in Gambit
http://dynamo.iro.umontreal.ca/wiki/images/a/a7/A_Tour_of_Scheme_in_Gambit.pdf - Gambit Scheme: Inside Out
http://www.iro.umontreal.ca/~gambit/Gambit-inside-out.pdf - Gambit Internal Documentation
http://dynamo.iro.umontreal.ca/wiki/index.php/Internal_Documentation - clojure-scheme: Compiling to Native Code via Scheme
http://www.iro.umontreal.ca/~gambit/Sorenson-Clojure-to-Native-via-Scheme.pdf - Gauche – a Scheme implementation
http://practical-scheme.net/gauche/ - Scheme48
https://s48.org/ - SISC (Second Interpreter of Scheme)
http://sisc-scheme.org/ - The SCM Implementation of Scheme
https://people.csail.mit.edu/jaffer/SCM.html - Ypsilon – The ultimate script language system for the video pinball fourth generation
http://www.littlewingpinball.com/doc/en/ypsilon/index.html - Chicken Scheme
https://call-cc.org/ - Eggs Unlimited
http://wiki.call-cc.org/chicken-projects/egg-index-5.html - Chicken Scheme Wiki
https://wiki.call-cc.org/ - CHICKEN for Python programmers
https://wiki.call-cc.org/chicken-for-python-programmers - Programming for Performance
http://wiki.call-cc.org/programming-for-performance - Using the compiler
https://wiki.call-cc.org/man/4/Using%20the%20compiler - CHICKEN Scheme tutorials
https://wiki.call-cc.org/tutorials - Traditional Turtles
https://docs.racket-lang.org/turtles/Traditional_Turtles.html - [racket] How best to repeat a function call n times?
https://lists.racket-lang.org/users/archive/2014-September/064203.html - Racket: Macros
https://www.it.uu.se/edu/course/homepage/avfunpro/ht13/lectures/Racket-3-Macros.pdf - Beautiful Racket / explainers: Macros
https://beautifulracket.com/explainer/macros.html - Macros (dokumentace k Racketu)
https://docs.racket-lang.org/guide/macros.html - Model syntaxe jazyka Racket
https://docs.racket-lang.org/reference/syntax-model.html - Syntax Objects
https://docs.racket-lang.org/guide/stx-obj.html - Tech behind Tech: Clojure Macros Simplified
http://techbehindtech.com/2010/09/28/clojure-macros-simplified/ - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - Beautiful Racket: an introduction to language-oriented programming using Racket
https://beautifulracket.com/ - Stránky projektu Racket
https://racket-lang.org/ - Dokumentace k projektu Racket
https://docs.racket-lang.org/index.html - Seznam dostupných balíčků pro Racket
https://pkgs.racket-lang.org/ - Racket na Wikipedii
https://en.wikipedia.org/wiki/Racket_(programming_language) - Vector Library (R7RS-compatible)
https://srfi.schemers.org/srfi-133/srfi-133.html - Blogy o Racketu a navazujících technologiích
https://blog.racket-lang.org/ - Prográmky psané v Racketu na RosettaCode
http://rosettacode.org/wiki/Category:Racket - Fear of Macros
https://www.greghendershott.com/fear-of-macros/ - Rackjure
https://github.com/greghendershott/rackjure - Matthew Flatt’s proposal to change Racket’s s-expressions based syntax to infix representation creates a stir in the community
https://hub.packtpub.com/matthew-flatts-proposal-to-change-rackets-s-expressions-based-syntax-to-infix-representation-creates-a-stir-in-the-community/ - Racket News
https://racket-news.com/ - Racket: Lisp for learning
https://lwn.net/Articles/795385/ - Future of Racket
https://www.greghendershott.com/2019/07/future-of-racket.html - Vectors (pro Gauche)
https://practical-scheme.net/gauche/man/gauche-refe/Vectors.html - Kawa: Compiling Scheme to Java
https://www.mit.edu/afs.new/sipb/project/kawa/doc/kawa-tour.html - Kawa in Languages shootout
http://per.bothner.com/blog/2010/Kawa-in-shootout/ - Kawa 2.0 Supports Scheme R7RS
https://developers.slashdot.org/story/14/12/13/2259225/kawa-20-supports-scheme-r7rs/ - Kawa — fast scripting on the Java platform
https://lwn.net/Articles/623349/ - Tail call (a její optimalizace)
https://en.wikipedia.org/wiki/Tail_call - SLIME (Wikipedia)
http://en.wikipedia.org/wiki/SLIME - slime.vim
http://s3.amazonaws.com/mps/slime.vim - What are the best scheme implementations?
https://www.slant.co/topics/5282/~scheme-implementations - Bigloo homepage
http://www-sop.inria.fr/mimosa/fp/Bigloo/ - FTP s tarbally Bigloo
ftp://ftp-sop.inria.fr/indes/fp/Bigloo - GOTO 2018 • Functional Programming in 40 Minutes • Russ Olsen
https://www.youtube.com/watch?v=0if71HOyVjY - TinyScheme (stránka na Sourceforge)
http://tinyscheme.sourceforge.net/home.html - Embedding Tiny Scheme in a Game
http://www.silicondelight.com/embedding-tiny-scheme-in-a-game/ - Embedding Scheme for a game mission scripting DSL
http://carloscarrasco.com/embedding-scheme-for-a-game-mission-scripting-dsl.html - Všechny verze TinyScheme na SourceForge
https://sourceforge.net/projects/tinyscheme/files/tinyscheme/ - Fork TinyScheme na GitHubu
https://github.com/yawnt/tinyscheme - Ackermannova funkce
https://cs.wikipedia.org/wiki/Ackermannova_funkce - Ackermann function na Rosetta Code
https://rosettacode.org/wiki/Ackermann_function#Scheme - Success Stories (lisp.org)
https://lisp-lang.org/success/ - Allegro Common Lisp Success Stories
https://franz.com/success/ - Clojure Success Stories
https://clojure.org/community/success_stories - Scheme Quick Reference
https://www.st.cs.uni-saarland.de/edu/config-ss04/scheme-quickref.pdf - Slajdy o Scheme (od slajdu číslo 15)
https://docs.google.com/presentation/d/1abmDnKjrq1tcjGvvRNAKhOiSTSE2lyagtcEPal07Gbo/edit - Scheme Cheat Sheet
https://github.com/smythp/scheme-cheat-sheet - Embedding Lua, embedding Guile
http://puntoblogspot.blogspot.com/2013/04/embedding-lua-embedding-guile.html - Lambda Papers
https://en.wikisource.org/wiki/Lambda_Papers - Revised7Report on the Algorithmic Language Scheme
https://small.r7rs.org/attachment/r7rs.pdf - Video Lectures (MIT, SICP 2005)
https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6–001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/ - Why is Scheme my first language in university?
https://softwareengineering.stackexchange.com/questions/115252/why-is-scheme-my-first-language-in-university - The Perils of JavaSchools
https://www.joelonsoftware.com/2005/12/29/the-perils-of-javaschools-2/ - How to Design Programs, Second Edition
https://htdp.org/2019–02–24/index.html - LilyPond
http://lilypond.org/ - LilyPond — Extending (přes Scheme)
http://lilypond.org/doc/v2.18/Documentation/extending/scheme-tutorial - Scheme in LilyPond
http://lilypond.org/doc/v2.18/Documentation/extending/scheme-in-lilypond - GnuCash
http://www.gnucash.org/ - Custom Reports (in GNU Cash)
https://wiki.gnucash.org/wiki/Custom_Reports - Program by Design
https://programbydesign.org/ - SchemePy
https://pypi.org/project/SchemePy/ - LISP FQA: Section – [1–5] What is the „minimal“ set of primitives needed for a Lisp interpreter?
http://www.faqs.org/faqs/lisp-faq/part1/section-6.html - femtolisp
https://github.com/JeffBezanson/femtolisp - (How to Write a (Lisp) Interpreter (in Python))
http://norvig.com/lispy.html - Repositář s Guile Emacsem
http://git.hcoop.net/?p=bpt/guile.git - Interacting with Guile Compound Data Types in C
http://www.lonelycactus.com/guilebook/x1555.html - Calling Guile functions from C
http://www.lonelycactus.com/guilebook/c1204.html#SECCALLGUILEFUNC - Arrays, and other compound data types
http://www.lonelycactus.com/guilebook/charrays.html - Interacting with Guile Compound Data Types in C
http://www.lonelycactus.com/guilebook/x1555.html - Guile Reference Manual
https://www.gnu.org/software/guile/manual/html_node/index.html - Scheme: Summary of Common Syntax
https://www.gnu.org/software/guile/manual/html_node/Syntax-Summary.html#Syntax-Summary - Scripting with Guile: Extension language enhances C and Scheme
https://www.ibm.com/developerworks/library/l-guile/index.html - Having fun with Guile: a tutorial
http://dustycloud.org/misc/guile-tutorial.html - Guile: Loading Readline Support
https://www.gnu.org/software/guile/manual/html_node/Loading-Readline-Support.html#Loading-Readline-Support - lispy
https://pypi.org/project/lispy/ - Lython
https://pypi.org/project/Lython/ - Lizpop
https://pypi.org/project/lizpop/ - Budoucnost programovacích jazyků
http://www.knesl.com/budoucnost-programovacich-jazyku - LISP Prolog and Evolution
http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html - List of Lisp-family programming languages
https://en.wikipedia.org/wiki/List_of_Lisp-family_programming_languages - clojure_py na indexu PyPi
https://pypi.python.org/pypi/clojure_py - PyClojure
https://github.com/eigenhombre/PyClojure - Hy na GitHubu
https://github.com/hylang/hy - Hy: The survival guide
https://notes.pault.ag/hy-survival-guide/ - Hy běžící na monitoru terminálu společnosti Symbolics
http://try-hy.appspot.com/ - Welcome to Hy’s documentation!
http://docs.hylang.org/en/stable/ - Hy na PyPi
https://pypi.org/project/hy/#description - Getting Hy on Python
https://lwn.net/Articles/596626/ - Programming Can Be Fun with Hy
https://opensourceforu.com/2014/02/programming-can-fun-hy/ - Přednáška o projektu Hy (pětiminutový lighttalk)
http://blog.pault.ag/day/2013/04/02 - Hy (Wikipedia)
https://en.wikipedia.org/wiki/Hy - GNU Emacs Lisp Reference Manual: Point
https://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html - GNU Emacs Lisp Reference Manual: Narrowing
https://www.gnu.org/software/emacs/manual/html_node/elisp/Narrowing.html - GNU Emacs Lisp Reference Manual: Functions that Create Markers
https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Markers.html - GNU Emacs Lisp Reference Manual: Motion
https://www.gnu.org/software/emacs/manual/html_node/elisp/Motion.html#Motion - GNU Emacs Lisp Reference Manual: Basic Char Syntax
https://www.gnu.org/software/emacs/manual/html_node/elisp/Basic-Char-Syntax.html - Elisp: Sequence: List, Array
http://ergoemacs.org/emacs/elisp_list_vs_vector.html - Elisp: Property List
http://ergoemacs.org/emacs/elisp_property_list.html - Elisp: Hash Table
http://ergoemacs.org/emacs/elisp_hash_table.html - Elisp: Association List
http://ergoemacs.org/emacs/elisp_association_list.html - The mapcar Function (An Introduction to Programming in Emacs Lisp)
https://www.gnu.org/software/emacs/manual/html_node/eintr/mapcar.html - Anaphoric macro
https://en.wikipedia.org/wiki/Anaphoric_macro - Some Common Lisp Loop Macro Examples
https://www.youtube.com/watch?v=3yl8o6r_omw - A Guided Tour of Emacs
https://www.gnu.org/software/emacs/tour/ - The Roots of Lisp
http://www.paulgraham.com/rootsoflisp.html - Evil (Emacs Wiki)
https://www.emacswiki.org/emacs/Evil - Evil (na GitHubu)
https://github.com/emacs-evil/evil - Evil (na stránkách repositáře MELPA)
https://melpa.org/#/evil - Evil Mode: How I Switched From VIM to Emacs
https://blog.jakuba.net/2014/06/23/evil-mode-how-to-switch-from-vim-to-emacs.html - GNU Emacs (home page)
https://www.gnu.org/software/emacs/ - GNU Emacs (texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs - An Introduction To Using GDB Under Emacs
http://tedlab.mit.edu/~dr/gdbintro.html - An Introduction to Programming in Emacs Lisp
https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html - 27.6 Running Debuggers Under Emacs
https://www.gnu.org/software/emacs/manual/html_node/emacs/Debuggers.html - GdbMode
http://www.emacswiki.org/emacs/GdbMode - Emacs (Wikipedia)
https://en.wikipedia.org/wiki/Emacs - Emacs timeline
http://www.jwz.org/doc/emacs-timeline.html - Emacs Text Editors Family
http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily - Vrapper aneb spojení možností Vimu a Eclipse
https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/ - Vrapper aneb spojení možností Vimu a Eclipse (část 2: vyhledávání a nahrazování textu)
https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse-cast-2-vyhledavani-a-nahrazovani-textu/ - Emacs/Evil-mode – A basic reference to using evil mode in Emacs
http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet - From Vim to Emacs+Evil chaotic migration guide
https://juanjoalvarez.net/es/detail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/ - Introduction to evil-mode {video)
https://www.youtube.com/watch?v=PeVQwYUxYEg - EINE (Emacs Wiki)
http://www.emacswiki.org/emacs/EINE - EINE (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?EINE - ZWEI (Emacs Wiki)
http://www.emacswiki.org/emacs/ZWEI - ZWEI (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?ZWEI - Zmacs (Wikipedia)
https://en.wikipedia.org/wiki/Zmacs - Zmacs (Texteditors.org)
http://texteditors.org/cgi-bin/wiki.pl?Zmacs - TecoEmacs (Emacs Wiki)
http://www.emacswiki.org/emacs/TecoEmacs - Micro Emacs
http://www.emacswiki.org/emacs/MicroEmacs - Micro Emacs (Wikipedia)
https://en.wikipedia.org/wiki/MicroEMACS - EmacsHistory
http://www.emacswiki.org/emacs/EmacsHistory - Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
http://www.finseth.com/emacs.html - evil-numbers
https://github.com/cofi/evil-numbers - Debuggery a jejich nadstavby v Linuxu (1.část)
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2.část)
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
https://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Org mode
https://orgmode.org/ - The Org Manual
https://orgmode.org/manual/index.html - Kakoune (modální textový editor)
http://kakoune.org/ - Vim-style keybinding in Emacs/Evil-mode
https://gist.github.com/troyp/6b4c9e1c8670200c04c16036805773d8 - Emacs – jak začít
http://www.abclinuxu.cz/clanky/navody/emacs-jak-zacit - Programovací jazyk LISP a LISP machines
https://www.root.cz/clanky/programovaci-jazyk-lisp-a-lisp-machines/ - Evil-surround
https://github.com/emacs-evil/evil-surround - Spacemacs
http://spacemacs.org/ - Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
http://hyperpolyglot.org/lisp - Common Lisp, Scheme, Clojure, And Elisp Compared
http://irreal.org/blog/?p=725 - Does Elisp Suck?
http://irreal.org/blog/?p=675 - Emacs pro mírně pokročilé (9): Elisp
https://www.root.cz/clanky/emacs-elisp/ - If I want to learn lisp, are emacs and elisp a good choice?
https://www.reddit.com/r/emacs/comments/2m141y/if_i_want_to_learn_lisp_are_emacs_and_elisp_a/ - Clojure(Script) Interactive Development Environment that Rocks!
https://github.com/clojure-emacs/cider - An Introduction to Emacs Lisp
https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html - Emergency Elisp
http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html - Lambda calculus
https://en.wikipedia.org/wiki/Lambda_calculus - John McCarthy's original LISP paper from 1959
https://www.reddit.com/r/programming/comments/17lpz4/john_mccarthys_original_lisp_paper_from_1959/ - Micro Manual LISP
https://www.scribd.com/document/54050141/Micro-Manual-LISP