Obsah
1. GNU Guile – interpret Scheme vestavitelný do nativních aplikací
4. Základní vlastnosti programovacího jazyka Scheme
5. Koncová rekurze (tail-recursion)
6. Některé rozdíly mezi LISPem a Scheme
9. Rozhraní mezi jazykem C a intepretrem Guile
10. Načtení skriptu a zavolání funkce bez parametrů z jazyka C
11. Překlad, slinkování a spuštění demonstračního příkladu
12. Zavolání funkce se dvěma parametry a zpracování návratové hodnoty
13. Předání řetězce do skriptu naprogramovaného v Guile
14. Zavolání céčkovské (nativní) funkce z Guile
15. Skript uložený přímo ve zdrojovém kódu ve formě řetězce
16. Předání tečka dvojice do skriptu vytvořeného v Guile
17. Konstrukce seznamu a vektoru v céčku s jeho předáním do Guile
18. Repositář s demonstračními příklady
1. GNU Guile – interpret Scheme vestavitelný do nativních aplikací
V dnešním článku se seznámíme s projektem nazvaným GNU Guile, což je implementace programovacího jazyka Scheme, kterou lze použít jak ve funkci běžného interpretru či JIT překladače, tak i – což je z praktického hlediska důležitější – je možné GNU Guile použít jako vestavěný skriptovací jazyk do nativních aplikací. GNU Guile se měl díky této vlastnosti stát primárním skriptovacím jazykem celého GNU projektu, v praxi se s ním ovšem setkáme pouze v některých aplikacích. Za zmínku stojí zejména projekty GnuCash [1] a LilyPond [2] [3]. GNU Guile se dále používá (i když nikoli jako jediný skriptovací jazyk) v GNOME a využit byl i v GIMPu, kde ho lze nahradit minimalistickým TinyScheme, což je projekt, o němž se zmíníme příště (typické je, že jak LilyPond, tak i GnuCash jsou interně dosti složité a uživatelsky skriptovatelné aplikace, u nichž je použití tak expresivního jazyka, jakým Scheme je, užitečné).
Pro zajímavost je vhodné se zmínit i projektu nazvaném Guile Emacs. Název tohoto projektu může být poněkud matoucí, protože se ve skutečnosti nejedná o snahu nahradit Emacs Lisp jazykem Scheme (resp. jeho konkrétní implementací GNU Guile), ale o zajištění, aby byly zdrojové kódy napsané v Elispu překládány stejným způsobem (podobným překladačem), jako je tomu v případě Guile. Výsledkem by měl být rychlejší běh jak samotného Emacsu, tak i jeho modulů, což může být zajímavé pro rozsáhlejší moduly typu org-mode, webového prohlížeče atd. Navíc se díky použití překladače GNU Guile otevírá možnost rozšíření možností samotného Elispu o vlastnosti podporované v GNU Guile. Dále by mělo být možné psát moduly buď v Elispu nebo přímo v Guile (které je sice taktéž založeno na LISPovském dialektu, ale jedná se o matematicky čistější implementaci, což některým programátorům může vyhovovat). Bližší informace o tomto projektu je možné nalézt na EmacsWiki, konkrétně na stránce https://www.emacswiki.org/emacs/GuileEmacs.
Dnešní článek je rozdělen na dvě části. V úvodní části se seznámíme s některými základními vlastnostmi programovacího jazyka Scheme (ovšem jen do té míry, do jaké budeme tento jazyk potřebovat v demonstračních příkladech) a v části druhé (začínající devátou kapitolou) se budeme věnovat té nejužitečnější vlastnosti GNU Guile – schopnosti vestavět tento programovací jazyk do nativních aplikací. Demonstrační příklady ze druhé části při překladu a linkování používají GNU Guile verze 2.0, protože právě tuto verzi nalezneme v některých „konzervativních“ distribucích Linuxu. Ovšem po nepatrné úpravě je možné přejít i na novější (a rychlejší!) GNU Guile 2.2. Více informací o GNU Guile verze 2.0 i verze 2.2 lze získat na stránkách https://www.gnu.org/software/guile/download/#releases a https://www.gnu.org/software/guile/news/gnu-guile-220-released.html.
2. Lambda Papers
První článek o programovacím jazyku Scheme, který měl poměrně velký vliv na další vývoj i chápání významu programovacích jazyků z teoretického i praktického hlediska, se jmenoval „Scheme: an Interpreter for Extended Lambda Calculus“ a vyšel až v roce 1975 (to již LISP skoro dosahoval plnoletosti). Po tomto článku následovala dvojice neméně důležitých publikací s všeříkajícími názvy „Lambda: The Ultimate Imperative“ a „Lambda: The Ultimate Declarative“ s popisem toho, jakým způsobem je možné ve Scheme nahrazovat běžné programové konstrukce známé například z tehdy používaných imperativních programovacích jazyků (céčka, Fortranu, Algolu, posléze i Pascalu atd.). V následující tabulce jsou vypsány všechny publikace Guye Steela a Geralda Sussmana vydávané v letech 1975 až 1980, které se dnes souhrnně nazývají Lambda Papers (https://en.wikisource.org/wiki/Lambda_Papers). I přes svoje stáří jsou tyto publikace stále velmi důležitou součástí celého oboru IT:
Rok vydání | Název publikace |
---|---|
1975 | Scheme: An Interpreter for Extended Lambda Calculus |
1976 | Lambda: The Ultimate Imperative |
1976 | Lambda: The Ultimate Declarative |
1977 | Debunking the ‚Expensive Procedure Call‘ Myth, or, Procedure Call Implementations Considered Harmful, or, Lambda: The Ultimate GOTO |
1978 | The Art of the Interpreter or, the Modularity Complex (Parts Zero, One, and Two) |
1978 | RABBIT: A Compiler for SCHEME |
1979 | Design of LISP-based Processors, or SCHEME: A Dielectric LISP, or Finite Memories Considered Harmful, or LAMBDA: The Ultimate Opcode |
1980 | Compiler Optimization Based on Viewing LAMBDA as RENAME + GOTO |
1980 | Design of a Lisp-based Processor |
3. Tajemná zkratka RnRS
Pro další vývoj programovacího jazyka Scheme byl důležitý rok 1978, kdy došlo ke vzniku jeho prvního skutečného standardu, jenž byl popsán v „Revidované zprávě o algoritmickém jazyku Scheme“ (Revised Report on the Algorithmic Language Scheme). Jazyk Scheme se i tomto datu samozřejmě dále vyvíjel, takže v roce 1985 vznikl další popis jeho standardu zveřejněný v dokumentu nazvaném „Revize revidované zprávy o Scheme, neboli neobvyklý LISP“. Tradice ve vydávání revizí (a revizí revizí) původní zprávy o programovacím jazyku Scheme zůstala zachována až do dnešní doby. Postupně (resp. přesněji řečeno prozatím) vzniklo celkem sedm revizí, což mj. znamená, že většina současných implementací tohoto programovacího jazyka stále odpovídá buď páté revizi („The Revised5 Report on the Algorithmic Language Scheme“) z roku 1998, přizpůsobuje se revizi šesté („The Revised6 Report on the Algorithmic Language Scheme“), která byla schválena v roce 2007, popř. již adaptovala poněkud kontroverzní R7RS (viz též http://scheme-reports.org/ popř. R7RSSmallErrata).
Odkazy na jednotlivé (revidované (revidované)) reporty:
- Report
- Revised Report
- Revised2 Report
- Revised3 Report
- Revised4 Report
- Revised5 Report
- Revised6 Report
- Revised7 Report, „small“ language
4. Základní vlastnosti programovacího jazyka Scheme
„Learning Lisp is like climbing a hill in which the first part is the steepest.“
Scheme je programovací jazyk, který podporuje různá paradigmata programování, především paradigma funkcionální (i když se nejedná o čistě funkcionální jazyk) a paradigma imperativní. Z funkcionálních jazyků se ve Scheme objevuje především koncept lambda výrazů, funkcí jakožto plnohodnotných datových typů, což mj. znamená, že funkce je možné předávat jako parametry jiným funkcím, funkce mohou být návratovými hodnotami jiných funkcí atd. Naopak z jazyků imperativních (mezi něž patří velká část v současnosti používaných programovacích jazyků) se ve Scheme objevuje bloková struktura kódu převzatá z Algolu 60, koncept globálních a lokálních proměnných s takzvanou lexikální oblastí jejich platnosti (lexical scope, na rozdíl od dynamického vyhodnocování platnosti, které bylo použito v původním LISPu a teprve později se v novějších LISPech přešlo k vyhodnocování lexikálnímu) a podpora programových smyček, které se v čistě funkcionálních jazycích nahrazují rekurzí či speciálními formami, mezi něž patří například apply, map, for-each, mapcar či reduce.
Základními datovými typy, se kterými se ve Scheme pracuje, jsou atomy a seznamy. Atomy jsou z hlediska tohoto programovacího jazyka základními objekty, které není možné dále dělit, ale je je možné ukládat do seznamů. Atomy mohou být několika typů: jedná se především o symboly (například ABC), čísla (42, 3.1415 atd. – některé interpretry jazyka Scheme rozlišují celá čísla, čísla reálná, čísla komplexní a někdy též zlomky, tj. čísla racionální), řetězce, vestavěné funkce atd. V reálných programech se atomy ukládají do seznamů, přičemž pro označení začátku a konce seznamu se používají kulaté závorky – levá závorka samozřejmě označuje začátek seznamu a pravá závorka jeho konec. Prvky/elementy seznamu jsou od sebe odděleny alespoň jednou mezerou nebo koncem řádku, což mj. znamená, že seznam může být rozepsán na více řádcích (to je velmi důležité ve chvíli, kdy se pomocí seznamů reprezentují funkce).
Zvláštním a v mnoha ohledech důležitým typem seznamu je prázdný seznam, jenž neobsahuje žádné prvky (elementy) a proto je zapisován levou závorkou, za níž ihned následuje závorka pravá. Mezi závorkami se tedy nenachází žádný atom ani další seznam, mohou se zde nacházet pouze mezery nebo konce řádků (v LISPu je navíc prázdný seznam ekvivalentní symbolu nil, který současně určuje pravdivostní hodnotu false). Seznam může jako své prvky (elementy) obsahovat jak atomy, tak i další vnořené seznamy, což znamená, že se jedná o rekurzivní datovou strukturu, pomocí níž je možné popsat i mnohé další složitější datové struktury, například n-dimenzionální pole, stromy, hierarchické mřížky atd. Pod tímto odstavcem je uvedeno několik příkladů seznamů akceptovaných interpretrem jazyka Scheme. Povšimněte si důsledného vyvážení pravých a levých závorek, především v případě, že seznam obsahuje jako své prvky/elementy další podseznamy:
(define (print item) (display item) (newline)) (print '(1 2 3 4)) (print (list 1 2 3 4)) ; create list and assign it to symbol ; (=variable) (define a '(1 2 3 4)) ; get the first item (print (car a)) ; get the rest of a list (print (cdr a)) ; combination of car+cdr (print (cadr a)) ; combination of cdr+cdr (print (cddr a))
Interně jsou seznamy reprezentovány prvky, přičemž každý prvek je tvořen takzvanou tečka-dvojicí, kterou si můžeme představit jako strukturu obsahující dvě hodnoty – buď ukazatel na další tečka-dvojici nebo hodnotu (včetně prázdné hodnoty):
(define (print item) (display item) (newline)) (print '(1 . 2)) (print '(1 . ((2 . 3) . 4))) (print '((1 . 2) . (3 . 4))) ; this is proper list in LISP, not in Scheme! (print '(1 . (2 . (3 . nil)))) ; this is proper list (print '(1 . (2 . (3 . ()))))
Výsledky:
(1 . 2) (1 (2 . 3) . 4) ((1 . 2) 3 . 4) (1 2 3 . nil) (1 2 3)
5. Koncová rekurze (tail-recursion)
V naprosté většině algoritmů se objevují bloky kódu, které se mají iterativně opakovat. Při programování s využitím funkcionálního paradigmatu se iterace vyjadřuje formou rekurze. Ta je samozřejmě ve Scheme podporována (mezi jediné známější jazyky, které rekurzi nepodporovaly, patřil původní FORTRAN a Basic), ovšem specifikace jazyka Scheme jde ještě dále, protože určuje, ve kterých případech je skutečná rekurze (při níž se parametry a návratové adresy musí ukládat na zásobník) nahrazena takzvanou koncovou rekurzí, což zjednodušeně řečeno znamená, že se namísto skutečného rekurzivního volání funkce interně provede obyčejný skok (koncový skok či koncové volání) bez nutnosti alokace místa na zásobníku pro parametry volané funkce a návratové adresy. Koncová rekurze představuje při správném použití velmi silnou programovací techniku, protože umožňuje zapisovat mnoho algoritmů v mnohdy elegantní rekurzivní formě, ovšem skutečné zpracování takto zapsaných algoritmů je stejně efektivní jako provádění programové smyčky (každou koncovou rekurzi lze nahradit smyčkou a naopak).
Klasickým příkladem rozdílu mezi normální (plnou, skutečnou) rekurzí a koncovou rekurzí je výpočet faktoriálu. Ten můžeme zapsat mnoha způsoby, například (jak je to v matematice obvyklé), rekurzivně:
(define (factorial n) (if (= n 0) ; podmínka pro ukončení rekurzivního zanořování 1 ; faktoriál nuly je definitoricky roven jedné (* n (factorial (- n 1))) ) )
Ve Scheme ovšem používáme nepatrně odlišný zápis, kde se uzavírací závorky umisťují za poslední výraz:
(define (factorial n) (if (= n 0) ; podmínka pro ukončení rekurzivního zanořování 1 ; faktoriál nuly je definitoricky roven jedné (* n (factorial (- n 1)))))
Z teoretického hlediska není na výše uvedené funkci nic nekorektního, ovšem při jejím praktickém používání brzy narazíme na limit způsobený omezenou velikostí zásobníku. Povšimněte si, že jazyk Scheme nemá velikost datových typů omezen na int či double tak, jako tomu je u některých dalších programovacích jazyků, překvapivě i u mnoha jazyků vysokoúrovňových, kde by programátor teoreticky čekal vyšší úroveň abstrakce:
(factorial 1) 1 (factorial 10) 3628800 (factorial 100) 9332621544394415268169923885626670049071 5968264381621468592963895217599993229915 6089414639761565182862536979208272237582 51185210916864000000000000000000000000 ; výsledek je pro potřeby článku rozdělen na čtyři řádky ; ovšem nyní výpočet zhavaruje (záleží na konkrétní verzi interpretru!) (factorial 1000) ERROR: Stack overflow ABORT: (stack-overflow)
Výše uvedený rekurzivní výpočet lze relativně malou úpravou převést na výpočet který (alespoň v programovacím jazyce Scheme) vede na koncové volání, což mj. znamená, že paměťové (prostorové) nároky tohoto programu jsou konstantní:
; výpočet faktoriálu využívající koncového volání (define (factorial n) (let fact-iter ( ; pomocná vnitřní funkce (n n) ; počitadlo iterací (result 1)) ; průběžný výsledek (if (= n 0) ; po dosažení koncového stavu result ; se vrátí průběžný výsledek (fact-iter (- n 1) (* n result)) ; koncové volání )))
O tom, že upravený algoritmus výpočtu faktoriálu nevyužívá zásobník pro ukládání mezivýsledků ani počitadla iterací, se můžeme jednoduše přesvědčit. Popravdě řečeno i tento výpočet pro nějaké velké n zhavaruje, protože se překročí limit paměti alokované pro uložení mezivýsledku – proměnné result:
(factorial 1) 1 (factorial 10) 3628800 (factorial 100) 9332621544394415268169923885626670049071 5968264381621468592963895217599993229915 6089414639761565182862536979208272237582 51185210916864000000000000000000000000 ; výsledek je pro potřeby článku rozdělen na čtyři řádky ; zkusíme výpočet faktoriálu pro nějaké ; větší číslo (factorial 1000) 4023872600770937735437024339230039857193 7486421071463254379991042993851239862902 0592044208486969404800479988610197196058 6316668729948085589013238296699445909974 2450408707375991882362772718873251977950 5950995276120874975462497043601418278094 6464962910563938874378864873371191810458 2578364784997701247663288983595573543251 3185323958463075557409114262417474349347 5534286465766116677973966688202912073791 4385371958824980812686783837455973174613 6085379534524221586593201928090878297308 4313928444032812315586110369768013573042 1616874760967587134831202547858932076716 9132448426236131412508780208000261683151 0273418279777047846358681701643650241536 9139828126481021309276124489635992870511 4964975419909342221566832572080821333186 1168115536158365469840467089756029009505 3761647584772842188967964624494516076535 3408198901385442487984959953319101723355 5566021394503997362807501378376153071277 6192684903435262520001588853514733161170 2103968175921510907788019393178114194545 2572238655414610628921879602238389714760 8850627686296714667469756291123408243920 8160153780889893964518263243671616762179 1689097799119037540312746222899880051954 4441428201218736174599264295658174662830 2955570299024324153181617210465832036786 9061172601587835207515162842255402651704 8330422614397428693306169089796848259012 5458327168226458066526769958652682272807 0757813918581788896522081643483448259932 6604336766017699961283186078838615027946 5955131156552036093988180612138558600301 4356945272242063446317974605946825731037 9008402443243846565724501440282188525247 0935190620929023136493273497565513958720 5596542287497740114133469627154228458623 7738753823048386568897646192738381490014 0767310446640259899490222221765904339901 8860185665264850617997023561938970178600 4081188972991831102117122984590164192106 8884387121855646124960798722908519296819 3723886426148396573822911231250241866493 5314397013742853192664987533721894069428 1434118520158014123344828015051399694290 1534830776445690990731524332782882698646 0278986432113908350621709500259738986355 4277196742822248757586765752344220207573 6305694988250879689281627538488633969099 5982628095612145099487170124451646126037 9029309120889086942028510640182154399457 1568059418727489980942547421735824010636 7740459574178516082923013535808184009699 6372524230560855903700624271243416909004 1536901059339838357779394109700277534720 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000 ; uff :-)
6. Některé rozdíly mezi LISPem a Scheme
Zápis programů v jazyku Scheme se ze syntaktického a částečně i ze sémantického hlediska podobá zápisu programů v LISPu, ovšem mezi oběma jazyky existuje několik rozdílů, které poněkud komplikují převody programů mezi LISPem a Scheme a samozřejmě i převody opačným směrem. Nejzásadnějším rozdílem mezi Scheme a mnohými staršími interpretry jazyka LISP (kromě Common Lispu) je to, že zatímco se v LISPu oblast platnosti proměnných stanovuje dynamicky v čase běhu programu (například ve chvíli, kdy je definována nová funkce se zjišťují hodnoty všech proměnných použitých v této funkci), ve Scheme a v některých novějších implementacích LISPu je oblast platnosti proměnné určena na základě toho, v jakém bloku se proměnná nachází (tato vlastnost jazyka se označuje lexical scope), což je podobné chování, jaké nalezneme i u naprosté většiny dalších programovacích jazyků. Lexical scope je v běžných programech přehlednější (programátor může určit oblast platnosti pouze ze zdrojového kódu, nemusí přemýšlet nad tím, jak se program chová při spuštění) a dokonce se i snáze implementuje.
Nejjednodušší příklad:
(define x 1) (define y 2) (define (add x y) (+ x y)) (print (+ x y)) (print (add x y)) ; vypíše se: 3 3
Změna hodnoty proměnné má vliv pouze na druhé volání funkce:
(define x 1) (define y 2) (define (add x y) (+ x y)) (print (add x y)) (set! x 10) (print (add x y)) ; vypíše se: 3 12
Zatím se neděje nic objevného, ovšem další příklad již ukazuje, jak Scheme pracuje s lokálním rozsahem proměnné:
(define x 1) (define y 2) (define (add x y) ; rozsah (scope) je lokální! (set! x (+ x y)) x) (print (add x y)) (print (add x y)) (set! x 10) (print (add x y)) (print (add x y)) ; vypíše se: 3 3 12 12
A konečně příklad s globálními a lokálními proměnnými:
(define x 1) (define y 2) (define (add x y) (+ x y)) (print (add x y)) (print (let ((x 10) (y 20)) (add x y))) (set! x 10) (print (add x y)) (print (let ((x 10) (y 20)) (add x y))) (print (let ((x 100)) (add x y))) ; vypíše se: 3 30 12 30 102
(define (larger-than limit) (lambda (value) (> value limit))) (print ((larger-than 5) 0)) (print ((larger-than 5) 10)) (print (filter (larger-than 5) '(1 2 3 4 5 6 7 8 9 10))) ; s výsledky #f #t (6 7 8 9 10) (6 7 8 9 10)
Druhou odlišností mezi Scheme a LISPem je rozdílná reprezentace pravdivostních hodnot. Zatímco LISP považuje prázdný seznam (), reprezentovaný též hodnotou nil, za nepravdu a všechny ostatní hodnoty za pravdu, existuje ve Scheme jen jediná globálně dostupná a v celém systému jedinečná nepravdivá hodnota označovaná symbolem #f. Všechny ostatní objekty, včetně prázdného seznamu, jsou považovány za hodnotu pravdivou, což může komplikovat převody zdrojových kódů programů, protože v poměrně velkém množství algoritmů se například zpracovávají seznamy takovým způsobem, že se z nich postupně odebírají prvky a algoritmus skončí v případě odebrání posledního prvku, neboť se prázdný seznam vyhodnotí na nepravdivou hodnotu.
7. Funkce
Podobně jako u každého dialektu programovacího jazyka LISP, i v případě Schme se program skládá především z funkcí. Ty mohou být anonymní (nepojmenované) či naopak pojmenované. Nejprve se zabývejme pojmenovanými funkcemi, protože ty se chovají prakticky stejně, jako běžné funkce v jiných programovacích jazycích. Pojmenované funkce se definují pomocí define, za nímž v závorkách následuje jméno funkce. Každá funkce může mít libovolný počet parametrů, jejichž jména se uvádí v seznamu ihned za pojmenováním funkce. Poslední částí formy define je v tomto případě tělo funkce, přičemž po zavolání funkce se vyhodnocená forma vrátí jako její výsledek (nikde se tedy nezapisuje slovo „return“ ani nic podobného):
; one-liner function (define (add x y) (+ x y)) ; function written on more lines (define (mul x y) (* x y))
; function written on more lines using lambda (define div (lambda (x y) (* x y)))
Zavolání funkce je jednoduché – používá se stále ten samý formát seznamu, na jehož prvním místě je jméno funkce a za ním následují parametry:
(print (add 1 2)) (print (mul 6 7)) (print (div 10 3))
Kromě pojmenovaných funkcí, které jsme si již představili v předchozím textu, je možné ve Scheme použít i funkce anonymní, tj. funkce, které nejsou navázány na žádné jméno. Pro tento účel se používá přímo lambda výraz (bez define), podobně jako v každém ortodoxním Lispu (snad kromě PicoLispu):
; anonymous function is a value (lambda (x y) (+ x y)) ; call anonymous function (print (lambda (x y) (+ x y)))
8. Speciální formy
Poslední důležitou vlastností jazyka Scheme, s níž se dnes seznámíme, je použití takzvaných speciálních forem. Ze syntaktického hlediska jsou speciální formy zapisovány naprosto stejným způsobem jako běžné funkce, ovšem existuje zde jeden významný rozdíl – zatímco u funkcí jsou všechny jejich parametry nejdříve vyhodnoceny, u speciálních forem k tomuto vyhodnocení obecně nedochází, resp. jsou vyhodnoceny pouze některé parametry (které konkrétně, to závisí na tom, o jakou speciální formu se jedná).
K čemu jsou speciální formy dobré? Typickým příkladem je zápis podmíněných bloků kódu. V tomto případě potřebujeme, aby se nějaká část programu vykonala pouze v případě, že je splněna (popř. nesplněna) nějaká podmínka, v opačném případě nemá být tato část programu vůbec vykonána. Pomocí běžných funkcí by nebylo možné tuto funkcionalitu splnit, protože by kód (předaný jako parametr – jinou možnost ve Scheme ostatně nemáme) vykonal ještě před zavoláním „podmínkové“ funkce. Z toho vyplývá, že samotná podmínka (i když se syntakticky podobá volání funkce) je speciální formou.
Následuje seznam základních speciálních forem ve Scheme:
# | Jméno | Stručný popis |
---|---|---|
1 | lambda | vytvoření anonymní funkce nebo uzávěru |
2 | define | definice nové proměnné (může jít i o funkci) |
3 | quote | zakazuje vyhodnocení podvýrazu (tedy seznamu) |
4 | set! | změna hodnoty proměnné |
5 | let | blok, na který je navázána nová lokální proměnná či proměnné |
6 | let* | podobné let, ovšem umožňuje při inicializaci proměnných použít proměnné nalevo (nahoře) od právě deklarované proměnné |
7 | letrec | podobné let, ovšem navíc je možné se při inicializaci proměnných rekurzivně odkazovat na další proměnné |
8 | letrec* | kombinace let* a letrec |
9 | begin | umožňuje definovat blok s více výrazy, které se postupně vyhodnotí |
10 | if | podmíněné vyhodnocení prvního či druhého podvýrazu na základě vyhodnocené podmínky |
11 | cond | vícenásobné rozvětvení (vyhodnocení podvýrazů) |
12 | case | rozeskok na základě hodnoty vyhodnoceného podvýrazu |
13 | when | pokud je podmínka splněna, vyhodnotí všechny podvýrazy |
14 | unless | pokud podmínka není splněna, vyhodnotí všechny podvýrazy |
15 | and | zkrácené vyhodnocení logického součinu |
16 | or | zkrácené vyhodnocení logického součtu |
17 | do | zápis iterace s inicializací logických proměnných i s jejich změnou v každé iteraci |
Někdy (spíše však v teoreticky zaměřených článcích) se setkáme i s pojmem primitivní speciální formy. Myslí se tím minimální sada speciálních forem, která sama o sobě dostačuje, aby bylo možné vytvořit plnohodnotný programovací jazyk. Existuje hned několik kombinací těchto primitivních speciálních forem, například:
# | Speciální forma |
---|---|
1 | lambda |
2 | define |
3 | quote |
4 | if |
Alternativní výběr:
# | Speciální forma |
---|---|
1 | lambda |
2 | define |
3 | quote |
4 | cond |
Další alternativní výběr:
# | Speciální forma |
---|---|
1 | lambda |
2 | define |
3 | quote |
4 | and |
5 | or |
9. Rozhraní mezi jazykem C a intepretrem Guile
Použití samotného interpretru GNU Guile sice může být v některých případech užitečné (a to nejenom pro účely výuky), ovšem největší síla tohoto projektu spočívá v tom, že je možné (a to navíc relativně snadným způsobem) integrovat interpret programovacího jazyka Scheme do nativních aplikací naprogramovaných například v céčku či C++. Díky tomu je umožněno vytvářet rozšiřitelné aplikace, což se (pochopitelně tam, kde to dává smysl) z dlouhodobého hlediska může vyplatit jak samotným tvůrcům aplikace, tak i jejím uživatelům (zjednodušeně řečeno: každý nový plugin, ať již ho naprogramoval kdokoli, zajistí zvýšení prestiže aplikace a samozřejmě pomůže i koncovým uživatelům). Příkladů rozšiřitelných aplikací, které se mj. i díky této technologii staly úspěšné, je celá řada – různé CAD systémy podporující uživatelské pluginy (klasickým příkladem je AutoCAD), kancelářské balíky taktéž umožňující tvorbu mnohdy i velmi důmyslných pluginů, rozšiřitelné webové prohlížeče, integrovaná „pluginovatelná“ vývojová prostředí typu VSCode, mnohé textové editory (Vim, Atom) atd.
Existuje poměrně velké množství programovacích jazyků, které se vkládají (embed) do aplikací, resp. přesněji řečeno, které jsou pro tento účel vhodné. V oblasti her se například často setkáme s programovacím jazykem Lua, kterému jsme se již na stránkách Rootu věnovali. Podobně se, především v novějších aplikacích, setkáme s vloženým interpretrem Pythonu, popř. u aplikací běžících nad JVM (virtuálním strojem Javy) se může jednat o programovací jazyk Groovy (zde se příliš neujal Jython, který již není aktivně vyvíjen).
Ve světě GNU se tímto „embedded“ jazykem (alespoň oficiálně) mělo stát právě Scheme realizované v dnes popisovaném projektu GNU Guile. A s GNU Guile se skutečně v některých aplikacích setkáme, i když je nutné říci, že snaha o jeho použití jakožto univerzálního skriptovacího embedded jazyka nebyla a není stoprocentně úspěšná, a to dokonce ani v tak klasických GNU projektech, jako je GNOME (Shell), kde by se podobně silný programovací jazyk mohl dobře uplatnit. Důvodů pro tento stav můžeme najít hned několik; osobně si ovšem myslím, že tím, jak se i do mainstreamových jazyků postupně dostávají myšlenky převzaté ze světa LISPu (což je příklad Pythonu, ale například i výše zmíněného Groovy), se poněkud ztrácí motivace k tomu LISPovské jazyky používat ve větší míře.
Nicméně i přesto může být zajímavé se podívat na to, jak je vlastně rozhraní mezí nativními aplikacemi a GNU Guile realizováno. V následujících kapitolách si ukážeme několik demonstračních příkladů naprogramovaných v céčku (konkrétně v C99, protože samotné Guile již ANSI C přímo nepodporuje). Již na úvod si musíme říct, že rozhraní je oboustranné, což mj. znamená, že céčková aplikace může volat funkci naprogramovanou v Guile (pochopitelně s předáním parametrů a přebráním výsledné návratové hodnoty) a naopak funkce naprogramovaná v Guile může volat céčkovou funkci, opět s předáváním parametrů a návratové hodnoty. Vzhledem k velkým rozdílům mezi oběma světy je nutné při předávání parametrů zajistit veškeré potřebné konverze, čímž se v demonstračních příkladech pochopitelně taktéž budeme zabývat.
10. Načtení skriptu a zavolání funkce bez parametrů z jazyka C
První demonstrační příklad, který bude používat rozhraní mezi céčkem a Guile, bude velmi jednoduchý a jeho činnost lze shrnout do několika bodů:
- Po spuštění (přeložené a slinkované) nativní aplikace se inicializuje virtuální stroj GNU Guile.
- Následně se načte skript nazvaný „script1.scm“. Tento skript obsahuje jedinou funkci pojmenovanou say-hello.
- V nativní aplikaci se vytvoří struktura obsahující (mimo dalších věcí) i vstupní bod do výše zmíněné funkce say-hello.
- Funkce naprogramovaná v GNU Guile se zavolá a jelikož nemá žádné parametry, bude její zavolání realizováno nativní funkcí.
- Po ukončení funkce se řízení předá zpět nativní aplikaci. Případná návratová hodnota je ignorována.
Pro zavolání funkce definované v Guile z céčka je možné použít následující nativní funkce, kterým se předá jak reference na volanou funkci, tak i argumenty:
SCM scm_call_1(SCM proc, SCM arg1); SCM scm_call_2(SCM proc, SCM arg1, SCM arg2); SCM scm_call_3(SCM proc, SCM arg1, SCM arg2, SCM arg3); SCM scm_call_4(SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4);
Část příkladu naprogramovaná v GNU Guile vypadá následovně:
(define (say-hello) (display "Hello world!"))
Samozřejmě si ukážeme i delší část vytvořenou v jazyku C:
#include <stdio.h> #include <libguile.h> int main( int argc, char **arg ) { SCM function; scm_init_guile(); scm_c_primitive_load("script1.scm"); function = scm_variable_ref(scm_c_lookup("say-hello")); scm_call_0(function); return 0; }
11. Překlad, slinkování a spuštění demonstračního příkladu
Při překladu a linkování nativní části dnešního prvního demonstračního příkladu budeme muset vyřešit jeden nepatrný problém – hlavičkový soubor knihovny guile a někdy ani vlastní knihovna neleží v těch adresářích, kde je očekává překladač céčka a linker. Příslušné cesty tedy budeme muset nějakým způsobem zjistit a předat je překladači (volba -I) a linkeru (volba -l). Pro tento účel je možné použít užitečný nástroj pkg-config, který příslušné cesty (a ovšem i další přepínače) dokáže zjistit a použít. Nástroj pkg-config spustíme celkem dvakrát: jednou pro zjištění cesty k hlavičkovým souborům a podruhé pro zjištění cesty ke knihovně, s níž se má naše aplikace slinkovat:
Příkaz | Význam |
---|---|
pkg-config –cflags-only-I guile-2.0 | vrátí cestu k hlavičkovým souborům |
pkg-config –libs guile-2.0 | vrátí cestu ke knihovně včetně jména knihovny |
Celý překlad a slinkování by měl vypadat následovně:
gcc `pkg-config --cflags-only-I guile-2.0` `pkg-config --libs guile-2.0` test1.c -o test1
Namísto jednoúčelového skriptu určeného pouze pro překlad a slinkování příkladu si samozřejmě můžeme vytvořit plnohodnotný soubor Makefile, jehož předností je snadná rozšiřitelnost. Jedna z možných forem takového souboru je zobrazena pod tímto odstavcem:
CC=gcc CFLAGS=-Wall -std=c99 -pedantic `pkg-config --cflags-only-I guile-2.0` LIBS=`pkg-config --libs guile-2.0` EXENAME=test1 all: ${EXENAME} clean: rm -f test1.o rm -f ${EXENAME} ${EXENAME}: test1.o ${CC} $< ${LIBS} -o $@ test1.o: test1.c ${CC} -c $< ${CFLAGS}
12. Zavolání funkce se dvěma parametry a zpracování návratové hodnoty
Ve druhém příkladu si ukážeme způsob volání funkce se dvěma parametry:
(define (multiply x y) (* x y))
Tuto funkci budeme muset z céčka zavolat pomocí:
SCM scm_call_2(SCM proc, SCM arg1, SCM arg2);
Návratová hodnota ze scm_call2 ovšem není přímo výsledek funkce v Guile, ale obalová struktura. Pro konverzi se použije konverzní funkce scm_to_int:
return_value = scm_call_2(function, scm_from_int(6), scm_from_int(7)); integer_result = scm_to_int(return_value);
Úplný kód druhého demonstračního příkladu vypadá takto:
#include <stdio.h> #include <libguile.h> int main( int argc, char **arg ) { SCM function, return_value; int integer_result; scm_init_guile(); scm_c_primitive_load("script2.scm"); function = scm_variable_ref(scm_c_lookup("multiply")); return_value = scm_call_2(function, scm_from_int(6), scm_from_int(7)); integer_result = scm_to_int(return_value); printf("result: %d\n", integer_result); return 0; }
13. Předání řetězce do skriptu naprogramovaného v Guile
Velmi často je taktéž nutné do Guile předávat řetězce, například do následující funkce určené pro vytištění předaného řetězce na terminál:
(define (print-message message) (begin (display message) (newline)))
Jedno z možných řešení spočívá v použití konverzní funkce scm_from_utf8_string:
scm_from_utf8_string("Hello world!")
Další část příkladu je prakticky stejná jako u příkladu prvního:
#include <stdio.h> #include <libguile.h> int main( int argc, char **arg ) { SCM function; scm_init_guile(); scm_c_primitive_load("script3.scm"); function = scm_variable_ref(scm_c_lookup("print-message")); scm_call_1(function, scm_from_utf8_string("Hello world!")); return 0; }
14. Zavolání céčkovské (nativní) funkce z Guile
Prozatím jsme z céčka volali funkce naprogramované v Guile. Ovšem možný je pochopitelně i opačný postup, kdy budeme potřebovat zavolat nativní funkci z Guile:
(define (call-factorial number) (c-factorial number))
Zde by měla být c-factorial nativní funkce pro výpočet faktoriálu (rekurzivně, v céčku bez tail rekurze):
long factorial(long n) { if (n<=1) { return 1; } else { return n * factorial(n-1); } }
Ve skutečnosti ovšem provedeme mezikrok, kdy v nové nativní funkci c_factorial zkonvertujeme parametry a následně i vypočtenou návratovou hodnotu:
SCM c_factorial(SCM arg) { long c_arg = scm_to_long(arg); long result = factorial(c_arg); printf("%ld! = %ld\n", c_arg, result); return scm_from_int(result); }
Tuto funkci budeme muset zaregistrovat, aby ji interpret Guile našel:
scm_c_define_gsubr("c-factorial", 1, 0, 0, (void*)c_factorial);
Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu vypadá následovně:
#include <stdio.h> #include <libguile.h> long factorial(long n) { if (n<=1) { return 1; } else { return n * factorial(n-1); } } SCM c_factorial(SCM arg) { long c_arg = scm_to_long(arg); long result = factorial(c_arg); printf("%ld! = %ld\n", c_arg, result); return scm_from_int(result); } int main( int argc, char **arg ) { SCM function; int n; scm_init_guile(); scm_c_primitive_load("script4.scm"); scm_c_define_gsubr("c-factorial", 1, 0, 0, (void*)c_factorial); function = scm_variable_ref(scm_c_lookup("call-factorial")); for (n=0; n<=10; n++) { scm_call_1(function, scm_from_int(n)); } return 0; }
15. Skript uložený přímo ve zdrojovém kódu ve formě řetězce
V některých případech může být výhodné mít kratší skript naprogramovaný v Guile reprezentovaný céčkovým řetězcem popř. si dokonce můžeme skript „složit“ z nějaké šablony, použít funkci printf atd. atd. Jak se takový skript spustí, je ukázáno v dnešním pátém demonstračním příkladu:
#include <stdio.h> #include <libguile.h> const char *script = "(define (say-hello)\n"\ " (display \"Hello world!\"))"; int main( int argc, char **arg ) { SCM function; scm_init_guile(); scm_c_eval_string(script); function = scm_variable_ref(scm_c_lookup("say-hello")); scm_call_0(function); return 0; }
16. Předání tečka dvojice do skriptu vytvořeného v Guile
Základními strukturovanými datovými typy jazyka Scheme jsou tečka-dvojice, seznamy a taktéž vektory. Poměrně často je nutné tyto datové struktury vytvářet v céčku a předávat je do Guile. Nejdříve si ukážeme způsob vytvoření tečka-dvojice, kterou následně zpracujeme tímto skriptem:
(define (print-pair pair) (begin (display pair) (newline) (display (* (car pair) (cdr pair))) (newline)))
Před vytvořením tečka dvojice zkonstruujeme dvojici hodnot (používáme zde historická jména car a cdr):
SCM s_car = scm_from_int(6); SCM s_cdr = scm_from_int(7);
Následně funkcí scm_cons tečka dvojici zkonstruujeme:
SCM s_pair = scm_cons(s_car, s_cdr);
S takto obalenou hodnotou se již pracuje stejně jako s jakoukoli jinou hodnotou, kterou do Guile potřebujeme předat:
#include <stdio.h> #include <libguile.h> int main( int argc, char **arg ) { SCM function; scm_init_guile(); SCM s_car = scm_from_int(6); SCM s_cdr = scm_from_int(7); SCM s_pair = scm_cons(s_car, s_cdr); scm_c_primitive_load("script6.scm"); function = scm_variable_ref(scm_c_lookup("print-pair")); scm_call_1(function, s_pair); return 0; }
17. Konstrukce seznamu a vektoru v céčku s jeho předáním do Guile
Poněkud složitější bude konstrukce seznamu, jenž budeme chtít zpracovat následujícím skriptem, který zobrazí délku seznamu i jeho obsah (v podobě čitelné člověkem):
(define (print-list lst) (begin (display (length lst)) (newline) (display lst) (newline)))
Nejprve vytvoříme prázdný seznam přestavovaný „zarážkou“ SCM_EOL:
SCM result_list = SCM_EOL;
Následně v programové smyčce vytvoříme jednotlivé elementy seznamu, převedeme je na jednoprvkové seznamy a funkcí scm_append je připojíme k postupně vznikajícímu výslednému seznamu:
for (i = 0; i < 100; i++) { SCM element = scm_from_int(i); SCM one_element_list = scm_list_1(element); result_list = scm_append(scm_list_2(result_list, one_element_list)); }
Úplný zdrojový kód dnešního předposledního demonstračního příkladu vypadá takto:
#include <stdio.h> #include <libguile.h> int main( int argc, char **arg ) { SCM function; SCM result_list = SCM_EOL; int i; scm_init_guile(); for (i = 0; i < 100; i++) { SCM element = scm_from_int(i); SCM one_element_list = scm_list_1(element); result_list = scm_append(scm_list_2(result_list, one_element_list)); } scm_c_primitive_load("script7.scm"); function = scm_variable_ref(scm_c_lookup("print-list")); scm_call_1(function, result_list); return 0; }
Dnešní poslední demonstrační příklad pracuje s vektory, jejichž délka je neměnitelná a musí být známá při jejich konstrukci (z tohoto pohledu jsou tedy vektory podobné běžně chápaným polím):
(define (print-vector lst) (begin (display (vector-length lst)) (newline) (display lst) (newline)))
Prázdný vektor se zkonstruuje funkcí scm_c_make_vector, které se předá počet prvků:
SCM vector = scm_c_make_vector(100, scm_from_int(0));
Následně již můžeme naplnit hodnoty jednotlivých prvků (prvky se nevytváří, na rozdíl od seznamů):
for (i = 0; i < 100; i++) { SCM element = scm_from_int(i); scm_vector_set_x(vector, scm_from_int(i), element); }
Celý zdrojový kód příkladu je vypsán pod tímto odstavcem:
#include <stdio.h> #include <libguile.h> int main( int argc, char **arg ) { SCM function; int i; scm_init_guile(); SCM vector = scm_c_make_vector(100, scm_from_int(0)); for (i = 0; i < 100; i++) { SCM element = scm_from_int(i); scm_vector_set_x(vector, scm_from_int(i), element); } scm_c_primitive_load("script8.scm"); function = scm_variable_ref(scm_c_lookup("print-vector")); scm_call_1(function, vector); return 0; }
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/lisp-families.git (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
19. Literatura
- 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 stránce 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
- 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 - How Lisp Became God's Own Programming Language
https://twobithistory.org/2018/10/14/lisp.html - History of Lisp
http://jmc.stanford.edu/articles/lisp/lisp.pdf - The Roots of Lisp
http://languagelog.ldc.upenn.edu/myl/llog/jmc.pdf - Racket
https://racket-lang.org/ - The Racket Manifesto
http://felleisen.org/matthias/manifesto/ - MIT replaces Scheme with Python
https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/ - Adventures in Advanced Symbolic Programming
http://groups.csail.mit.edu/mac/users/gjs/6.945/ - Why MIT Switched from Scheme to Python (2009)
https://news.ycombinator.com/item?id=14167453 - Starodávná stránka XLispu
http://www.xlisp.org/ - AutoLISP
https://en.wikipedia.org/wiki/AutoLISP - Seriál PicoLisp: minimalistický a výkonný interpret Lispu
https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/ - Common Lisp
https://common-lisp.net/ - Getting Going with Common Lisp
https://cliki.net/Getting%20Started - Online Tutorial (Common Lisp)
https://cliki.net/online%20tutorial - Guile Emacs
https://www.emacswiki.org/emacs/GuileEmacs - Guile Emacs History
https://www.emacswiki.org/emacs/GuileEmacsHistory - Guile is a programming language
https://www.gnu.org/software/guile/ - MIT Scheme
http://groups.csail.mit.edu/mac/projects/scheme/ - SIOD: Scheme in One Defun
http://people.delphiforums.com/gjc//siod.html - CommonLispForEmacs
https://www.emacswiki.org/emacs/CommonLispForEmacs - Elisp: print, princ, prin1, format, message
http://ergoemacs.org/emacs/elisp_printing.html - Special Forms in Lisp
http://www.nhplace.com/kent/Papers/Special-Forms.html - Basic Building Blocks in LISP
https://www.tutorialspoint.com/lisp/lisp_basic_syntax.htm - Introduction to LISP – University of Pittsburgh
https://people.cs.pitt.edu/~milos/courses/cs2740/Lectures/LispTutorial.pdf - Why don't people use LISP
https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/ - Structured program theorem
https://en.wikipedia.org/wiki/Structured_program_theorem - Clojure: API Documentation
https://clojure.org/api/api - Tutorial for the Common Lisp Loop Macro
http://www.ai.sri.com/pkarp/loop.html - Common Lisp's Loop Macro Examples for Beginners
http://www.unixuser.org/~euske/doc/cl/loop.html - A modern list api for Emacs. No 'cl required.
https://github.com/magnars/dash.el - The LOOP Facility
http://www.lispworks.com/documentation/HyperSpec/Body/06_a.htm - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - Transient Data Structureshttp://clojure.org/transients
- Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Clojure (na Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (na Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - SICP (The Structure and Interpretation of Computer Programs)
http://mitpress.mit.edu/sicp/ - Pure function
http://en.wikipedia.org/wiki/Pure_function - Funkcionální programování
http://cs.wikipedia.org/wiki/Funkcionální_programování - Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM
https://www.root.cz/clanky/jazyky-hy-a-clojure-py-moderni-dialekty-lispu-urcene-pro-python-vm/ - Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/ - Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
https://www.root.cz/clanky/programovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/