GNU Guile – interpret Scheme vestavitelný do nativních aplikací

16. 7. 2019
Doba čtení: 41 minut

Sdílet

 Autor: Conrad Barski, podle licence: Public domain
Ve druhé části miniseriálu o různých interpretrech a překladačích programovacích jazyků LISP a Scheme si ukážeme některé možnosti nabízené projektem GNU Guile. Zaměříme se přitom nejenom na samotný programovací jazyk, ale především na možnost integrovat GNU Guile do vlastních aplikací (embedding).

Obsah

1. GNU Guile – interpret Scheme vestavitelný do nativních aplikací

2. Lambda Papers

3. Tajemná zkratka RnRS

4. Základní vlastnosti programovacího jazyka Scheme

5. Koncová rekurze (tail-recursion)

6. Některé rozdíly mezi LISPem a Scheme

7. Funkce

8. Speciální formy

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

19. Literatura

20. Odkazy na Internetu

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/e­macs/GuileEmacs.

Poznámka: GUILE by se měl psát velkými písmeny, protože to je zkratka, která znamená „GNU Ubiquitous Intelligent Language for Extensions“. Plné jméno „GNU Guile“ tedy vlastně obsahuje „GNU“ hned dvakrát. Navíc je GNU rekurzivní zkratka znamenající „GNU's Not Unix!“, takže je zde poněkud „přepakoňováno“.

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/softwa­re/guile/download/#releases a https://www.gnu.org/softwa­re/guile/news/gnu-guile-220-released.html.

Poznámka: pokud vás zajímá, jak populární či naopak méně používaný je programovací jazyk Scheme, můžete navštívit například na stránku https://madnight.github.i­o/githut/#/pushes/2018/2, která obsahuje statistiky získané z veřejných repositářů na GitHubu (což ovšem na druhou stranu nemusí odpovídat tomu, jak se Scheme (ne)používá v projektech, které na GitHubu ve veřejných repositářích nejsou).

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.or­g/wiki/Lambda_Papers). I přes svoje stáří jsou tyto publikace stále velmi důležitou součástí celého oboru IT:

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:

  1. Report
  2. Revised Report
  3. Revised2 Report
  4. Revised3 Report
  5. Revised4 Report
  6. Revised5 Report
  7. Revised6 Report
  8. 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 :-)
Poznámka: GNU Guile používá dynamicky alokovaný zásobník, takže bez problémů dokáže vypočítat i relativně vysoké faktoriály, a to i bez použití tail rekurze.

6. Některé rozdíly mezi LISPem a Scheme

Poznámka: připomeňme si, že LISP je nutné chápat spíše jako koncept, než jako konkrétní programovací jazyk s přesně definovanými názvy literálů, funkcí a speciálních forem. V některých diskusích se ovšem pod obecným pojmem LISP myslí přímo Common LISP, což je velmi významný dialekt, kterému se budeme podrobněji věnovat v navazujících článcích.

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
Poznámka: lexical scope má ovšem dalekosáhlejší důsledky, které mj. ovlivňují činnost správce paměti atd. Jde 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 tato proměnná 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):
(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)
Poznámka: podívejte se i na další demonstrační příklady s uzávěry, které jsou uvedeny v osmnácté kapitole.

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.

Poznámka: ve skutečnosti i zde existují lokální odlišnosti, například ve chvíli, kdy se GNU Guile snaží emulovat některé vlastnosti Emacs Lispu.

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))
Poznámka: ve skutečnosti je výše uvedená definice pouze syntaktickým cukrem nahrazujícím definici proměnné, jejíž hodnotou je anonymní funkce, která je zapisovaná pomocí speciální formy lambda. Bez použití syntaktického cukru by definice nové funkce vypadala takto:
; 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
Poznámka: v poslední tabulce zdánlivě nenajdeme žádnou speciální formu, kterou by bylo možné použít pro realizaci rozhodovací konstrukce typu if nebo cond. Ve skutečnosti však speciální formy and a or používají takzvané zkrácené vyhodnocování výrazů (ostatně proto je nelze definovat jako běžné funkce) – pokud je z již vyhodnocených výrazů zřejmé, jaký bude výsledek logického součinu nebo součtu, další výrazy se již nevyhodnocují, i když mohou mít vedlejší efekt.

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ů:

  1. Po spuštění (přeložené a slinkované) nativní aplikace se inicializuje virtuální stroj GNU Guile.
  2. Následně se načte skript nazvaný „script1.scm“. Tento skript obsahuje jedinou funkci pojmenovanou say-hello.
  3. 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.
  4. Funkce naprogramovaná v GNU Guile se zavolá a jelikož nemá žádné parametry, bude její zavolání realizováno nativní funkcí.
  5. 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;
}
Poznámka: povšimněte si způsobu načtení skriptu i nalezení reference na volanou funkci.

11. Překlad, slinkování a spuštění demonstračního příkladu

Poznámka: v příkladech budeme používat Guile verze 2.0, ovšem úprava pro verzi 2.2 bude triviální.

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;
}
Poznámka: seznam i vektor lze v případě potřeby vytvořit i z céčkovského pole.

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ů):

bitcoin_skoleni

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:

# Příklad Popis příkladu Cesta
1 dot_pairs.scm konstrukce tečka dvojic https://github.com/tisnik/lisp-families/blob/master/exam­ples/dot_pairs.scm
2 cons.scm funkce cons https://github.com/tisnik/lisp-families/blob/master/examples/cons.scm
3 lists.scm práce se seznamy https://github.com/tisnik/lisp-families/blob/master/exam­ples/lists.scm
4 functions.scm deklarace a volání funkcí https://github.com/tisnik/lisp-families/blob/master/exam­ples/functions.scm
5 factorial1.scm rekurzivní výpočet faktoriálu, první varianta https://github.com/tisnik/lisp-families/blob/master/exam­ples/factorial1.scm
6 factorial2.scm rekurzivní výpočet faktoriálu, druhá varianta https://github.com/tisnik/lisp-families/blob/master/exam­ples/factorial2.scm
7 factorial3.scm tail rekurze při výpočtu faktoriálu https://github.com/tisnik/lisp-families/blob/master/exam­ples/factorial3.scm
8 lexical_scope1.scm lexikální oblast platnosti proměnných, příklad 1 https://github.com/tisnik/lisp-families/blob/master/exam­ples/lexical_scope1.scm
9 lexical_scope2.scm lexikální oblast platnosti proměnných, příklad 2 https://github.com/tisnik/lisp-families/blob/master/exam­ples/lexical_scope2.scm
10 lexical_scope3.scm lexikální oblast platnosti proměnných, příklad 3 https://github.com/tisnik/lisp-families/blob/master/exam­ples/lexical_scope3.scm
11 lexical_scope4.scm lexikální oblast platnosti proměnných, příklad 4 https://github.com/tisnik/lisp-families/blob/master/exam­ples/lexical_scope4.scm
12 closure1.scm uzávěry, příklad 1 https://github.com/tisnik/lisp-families/blob/master/exam­ples/closure1.scm
13 closure2.scm uzávěry, příklad 2 https://github.com/tisnik/lisp-families/blob/master/exam­ples/closure2.scm
14 closure3.scm uzávěry, příklad 3 https://github.com/tisnik/lisp-families/blob/master/exam­ples/closure3.scm
15 closure4.scm uzávěry, příklad 4 https://github.com/tisnik/lisp-families/blob/master/exam­ples/closure4.scm
       
16 test1 C a Guile: načtení skriptu a zavolání funkce bez parametrů https://github.com/tisnik/lisp-families/blob/master/c-interface/test1/
17 test2 C a Guile: zavolání funkce se dvěma parametry a zpracování návratové hodnoty https://github.com/tisnik/lisp-families/blob/master/c-interface/test2/
18 test3 C a Guile: předání řetězce https://github.com/tisnik/lisp-families/blob/master/c-interface/test3/
19 test4 C a Guile: zavolání céčkovské (nativní) funkce z Guile https://github.com/tisnik/lisp-families/blob/master/c-interface/test4/
20 test5 C a Guile: skript uložený přímo ve zdrojovém kódu ve formě řetězce https://github.com/tisnik/lisp-families/blob/master/c-interface/test5/
21 test6 C a Guile: předání tečka dvojice do skriptu vytvořeného v Guile https://github.com/tisnik/lisp-families/blob/master/c-interface/test6/
22 test7 C a Guile: konstrukce seznamu s jeho předáním do Guile https://github.com/tisnik/lisp-families/blob/master/c-interface/test7/
23 test8 C a Guile: konstrukce vektoru v céčku s jeho předáním do Guile https://github.com/tisnik/lisp-families/blob/master/c-interface/test8/

19. Literatura

  1. Peter Seibel
    „Practical Common Lisp“
    2009
  2. Paul Graham
    „ANSI Common Lisp“
    1995
  3. Gerald Gazdar
    „Natural Language Processing in Lisp: An Introduction to Computational Linguistics“
    1989
  4. Peter Norvig
    „Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp“
    1991
  5. Alex Mileler et.al.
    „Clojure Applied: From Practice to Practitioner“
    2015
  6. „Living Clojure: An Introduction and Training Plan for Developers“
    2015
  7. Dmitri Sotnikov
    „Web Development with Clojure: Build Bulletproof Web Apps with Less Code“
    2016
  8. McCarthy
    „Recursive functions of symbolic expressions and their computation by machine, part I“
    1960
  9. R. Kent Dybvig
    „The Scheme Programming Language“
    2009
  10. Max Hailperin
    „Concrete Abstractions“
    1998
  11. Guy L. Steele
    „History of Scheme“
    2006, Sun Microsystems Laboratories
  12. Kolář J., Muller K.:
    „Speciální programovací jazyky“
    Praha 1981
  13. „AutoLISP Release 9, Programmer's reference“
    Autodesk Ltd., October 1987
  14. „AutoLISP Release 10, Programmer's reference“
    Autodesk Ltd., September 1988
  15. 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
  16. Carl Hewitt; Peter Bishop and Richard Steiger
    „A Universal Modular Actor Formalism for Artificial Intelligence“
    1973
  17. Feiman, J.
    „The Gartner Programming Language Survey (October 2001)“
    Gartner Advisory
  18. 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)
  19. Paul Graham
    On Lisp
    Prentice Hall, 1993
    Dostupné online na stránce http://www.paulgraham.com/on­lisptext.html
  20. David S. Touretzky
    Common LISP: A Gentle Introduction to Symbolic Computation (Dover Books on Engineering)
  21. Peter Norvig
    Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp
  22. Patrick Winston, Berthold Horn
    Lisp (3rd Edition)
    ISBN-13: 978–0201083194, ISBN-10: 0201083191
  23. 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

  1. Scheme Quick Reference
    https://www.st.cs.uni-saarland.de/edu/config-ss04/scheme-quickref.pdf
  2. Slajdy o Scheme (od slajdu číslo 15)
    https://docs.google.com/pre­sentation/d/1abmDnKjrq1tcjGvvRNAK­hOiSTSE2lyagtcEPal07Gbo/e­dit
  3. Scheme Cheat Sheet
    https://github.com/smythp/scheme-cheat-sheet
  4. Embedding Lua, embedding Guile
    http://puntoblogspot.blog­spot.com/2013/04/embedding-lua-embedding-guile.html
  5. Lambda Papers
    https://en.wikisource.org/wi­ki/Lambda_Papers
  6. Revised7Report on the Algorithmic Language Scheme
    https://small.r7rs.org/at­tachment/r7rs.pdf
  7. 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/
  8. Why is Scheme my first language in university?
    https://softwareengineerin­g.stackexchange.com/questi­ons/115252/why-is-scheme-my-first-language-in-university
  9. The Perils of JavaSchools
    https://www.joelonsoftware­.com/2005/12/29/the-perils-of-javaschools-2/
  10. How to Design Programs, Second Edition
    https://htdp.org/2019–02–24/index.html
  11. LilyPond
    http://lilypond.org/
  12. LilyPond — Extending (přes Scheme)
    http://lilypond.org/doc/v2­.18/Documentation/extendin­g/scheme-tutorial
  13. Scheme in LilyPond
    http://lilypond.org/doc/v2­.18/Documentation/extendin­g/scheme-in-lilypond
  14. GnuCash
    http://www.gnucash.org/
  15. Custom Reports (in GNU Cash)
    https://wiki.gnucash.org/wi­ki/Custom_Reports
  16. Program by Design
    https://programbydesign.org/
  17. SchemePy
    https://pypi.org/project/SchemePy/
  18. 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
  19. femtolisp
    https://github.com/JeffBe­zanson/femtolisp
  20. (How to Write a (Lisp) Interpreter (in Python))
    http://norvig.com/lispy.html
  21. Repositář s Guile Emacsem
    http://git.hcoop.net/?p=bpt/guile.git
  22. Interacting with Guile Compound Data Types in C
    http://www.lonelycactus.com/gu­ilebook/x1555.html
  23. Calling Guile functions from C
    http://www.lonelycactus.com/gu­ilebook/c1204.html#SECCAL­LGUILEFUNC
  24. Arrays, and other compound data types
    http://www.lonelycactus.com/gu­ilebook/charrays.html
  25. Interacting with Guile Compound Data Types in C
    http://www.lonelycactus.com/gu­ilebook/x1555.html
  26. Guile Reference Manual
    https://www.gnu.org/softwa­re/guile/manual/html_node/in­dex.html
  27. Scheme: Summary of Common Syntax
    https://www.gnu.org/softwa­re/guile/manual/html_node/Syn­tax-Summary.html#Syntax-Summary
  28. Scripting with Guile: Extension language enhances C and Scheme
    https://www.ibm.com/develo­perworks/library/l-guile/index.html
  29. Having fun with Guile: a tutorial
    http://dustycloud.org/misc/guile-tutorial.html
  30. Guile: Loading Readline Support
    https://www.gnu.org/softwa­re/guile/manual/html_node/Lo­ading-Readline-Support.html#Loading-Readline-Support
  31. lispy
    https://pypi.org/project/lispy/
  32. Lython
    https://pypi.org/project/Lython/
  33. Lizpop
    https://pypi.org/project/lizpop/
  34. Budoucnost programovacích jazyků
    http://www.knesl.com/budoucnost-programovacich-jazyku
  35. LISP Prolog and Evolution
    http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html
  36. List of Lisp-family programming languages
    https://en.wikipedia.org/wi­ki/List_of_Lisp-family_programming_languages
  37. clojure_py na indexu PyPi
    https://pypi.python.org/py­pi/clojure_py
  38. PyClojure
    https://github.com/eigenhom­bre/PyClojure
  39. Hy na GitHubu
    https://github.com/hylang/hy
  40. Hy: The survival guide
    https://notes.pault.ag/hy-survival-guide/
  41. Hy běžící na monitoru terminálu společnosti Symbolics
    http://try-hy.appspot.com/
  42. Welcome to Hy’s documentation!
    http://docs.hylang.org/en/stable/
  43. Hy na PyPi
    https://pypi.org/project/hy/#des­cription
  44. Getting Hy on Python
    https://lwn.net/Articles/596626/
  45. Programming Can Be Fun with Hy
    https://opensourceforu.com/2014/02/pro­gramming-can-fun-hy/
  46. Přednáška o projektu Hy (pětiminutový lighttalk)
    http://blog.pault.ag/day/2013/04/02
  47. Hy (Wikipedia)
    https://en.wikipedia.org/wiki/Hy
  48. GNU Emacs Lisp Reference Manual: Point
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Point.html
  49. GNU Emacs Lisp Reference Manual: Narrowing
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Narrowing.html
  50. GNU Emacs Lisp Reference Manual: Functions that Create Markers
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Creating-Markers.html
  51. GNU Emacs Lisp Reference Manual: Motion
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Motion.html#Motion
  52. GNU Emacs Lisp Reference Manual: Basic Char Syntax
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­lisp/Basic-Char-Syntax.html
  53. Elisp: Sequence: List, Array
    http://ergoemacs.org/emac­s/elisp_list_vs_vector.html
  54. Elisp: Property List
    http://ergoemacs.org/emac­s/elisp_property_list.html
  55. Elisp: Hash Table
    http://ergoemacs.org/emac­s/elisp_hash_table.html
  56. Elisp: Association List
    http://ergoemacs.org/emac­s/elisp_association_list.html
  57. The mapcar Function (An Introduction to Programming in Emacs Lisp)
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/mapcar.html
  58. Anaphoric macro
    https://en.wikipedia.org/wi­ki/Anaphoric_macro
  59. Some Common Lisp Loop Macro Examples
    https://www.youtube.com/wat­ch?v=3yl8o6r_omw
  60. A Guided Tour of Emacs
    https://www.gnu.org/softwa­re/emacs/tour/
  61. The Roots of Lisp
    http://www.paulgraham.com/ro­otsoflisp.html
  62. Evil (Emacs Wiki)
    https://www.emacswiki.org/emacs/Evil
  63. Evil (na GitHubu)
    https://github.com/emacs-evil/evil
  64. Evil (na stránkách repositáře MELPA)
    https://melpa.org/#/evil
  65. Evil Mode: How I Switched From VIM to Emacs
    https://blog.jakuba.net/2014/06/23/e­vil-mode-how-to-switch-from-vim-to-emacs.html
  66. GNU Emacs (home page)
    https://www.gnu.org/software/emacs/
  67. GNU Emacs (texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs
  68. An Introduction To Using GDB Under Emacs
    http://tedlab.mit.edu/~dr/gdbin­tro.html
  69. An Introduction to Programming in Emacs Lisp
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/index.html
  70. 27.6 Running Debuggers Under Emacs
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­macs/Debuggers.html
  71. GdbMode
    http://www.emacswiki.org/e­macs/GdbMode
  72. Emacs (Wikipedia)
    https://en.wikipedia.org/wiki/Emacs
  73. Emacs timeline
    http://www.jwz.org/doc/emacs-timeline.html
  74. Emacs Text Editors Family
    http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily
  75. Vrapper aneb spojení možností Vimu a Eclipse
    https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/
  76. 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/
  77. Emacs/Evil-mode – A basic reference to using evil mode in Emacs
    http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet
  78. From Vim to Emacs+Evil chaotic migration guide
    https://juanjoalvarez.net/es/de­tail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/
  79. Introduction to evil-mode {video)
    https://www.youtube.com/wat­ch?v=PeVQwYUxYEg
  80. EINE (Emacs Wiki)
    http://www.emacswiki.org/emacs/EINE
  81. EINE (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?EINE
  82. ZWEI (Emacs Wiki)
    http://www.emacswiki.org/emacs/ZWEI
  83. ZWEI (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?ZWEI
  84. Zmacs (Wikipedia)
    https://en.wikipedia.org/wiki/Zmacs
  85. Zmacs (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?Zmacs
  86. TecoEmacs (Emacs Wiki)
    http://www.emacswiki.org/e­macs/TecoEmacs
  87. Micro Emacs
    http://www.emacswiki.org/e­macs/MicroEmacs
  88. Micro Emacs (Wikipedia)
    https://en.wikipedia.org/wi­ki/MicroEMACS
  89. EmacsHistory
    http://www.emacswiki.org/e­macs/EmacsHistory
  90. Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
    http://www.finseth.com/emacs.html
  91. evil-numbers
    https://github.com/cofi/evil-numbers
  92. Debuggery a jejich nadstavby v Linuxu (1.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  93. Debuggery a jejich nadstavby v Linuxu (2.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  94. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  95. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  96. 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/
  97. Org mode
    https://orgmode.org/
  98. The Org Manual
    https://orgmode.org/manual/index.html
  99. Kakoune (modální textový editor)
    http://kakoune.org/
  100. Vim-style keybinding in Emacs/Evil-mode
    https://gist.github.com/tro­yp/6b4c9e1c8670200c04c16036805773d8
  101. Emacs – jak začít
    http://www.abclinuxu.cz/clan­ky/navody/emacs-jak-zacit
  102. Programovací jazyk LISP a LISP machines
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lisp-a-lisp-machines/
  103. Evil-surround
    https://github.com/emacs-evil/evil-surround
  104. Spacemacs
    http://spacemacs.org/
  105. Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
    http://hyperpolyglot.org/lisp
  106. Common Lisp, Scheme, Clojure, And Elisp Compared
    http://irreal.org/blog/?p=725
  107. Does Elisp Suck?
    http://irreal.org/blog/?p=675
  108. Emacs pro mírně pokročilé (9): Elisp
    https://www.root.cz/clanky/emacs-elisp/
  109. If I want to learn lisp, are emacs and elisp a good choice?
    https://www.reddit.com/r/e­macs/comments/2m141y/if_i_wan­t_to_learn_lisp_are_emacs_an­d_elisp_a/
  110. Clojure(Script) Interactive Development Environment that Rocks!
    https://github.com/clojure-emacs/cider
  111. An Introduction to Emacs Lisp
    https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html
  112. Emergency Elisp
    http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html
  113. Lambda calculus
    https://en.wikipedia.org/wi­ki/Lambda_calculus
  114. John McCarthy's original LISP paper from 1959
    https://www.reddit.com/r/pro­gramming/comments/17lpz4/joh­n_mccarthys_original_lisp_pa­per_from_1959/
  115. Micro Manual LISP
    https://www.scribd.com/do­cument/54050141/Micro-Manual-LISP
  116. How Lisp Became God's Own Programming Language
    https://twobithistory.org/2018/10/14/lis­p.html
  117. History of Lisp
    http://jmc.stanford.edu/ar­ticles/lisp/lisp.pdf
  118. The Roots of Lisp
    http://languagelog.ldc.upen­n.edu/myl/llog/jmc.pdf
  119. Racket
    https://racket-lang.org/
  120. The Racket Manifesto
    http://felleisen.org/matthi­as/manifesto/
  121. MIT replaces Scheme with Python
    https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/
  122. Adventures in Advanced Symbolic Programming
    http://groups.csail.mit.e­du/mac/users/gjs/6.945/
  123. Why MIT Switched from Scheme to Python (2009)
    https://news.ycombinator.com/i­tem?id=14167453
  124. Starodávná stránka XLispu
    http://www.xlisp.org/
  125. AutoLISP
    https://en.wikipedia.org/wi­ki/AutoLISP
  126. Seriál PicoLisp: minimalistický a výkonný interpret Lispu
    https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/
  127. Common Lisp
    https://common-lisp.net/
  128. Getting Going with Common Lisp
    https://cliki.net/Getting%20Started
  129. Online Tutorial (Common Lisp)
    https://cliki.net/online%20tutorial
  130. Guile Emacs
    https://www.emacswiki.org/e­macs/GuileEmacs
  131. Guile Emacs History
    https://www.emacswiki.org/e­macs/GuileEmacsHistory
  132. Guile is a programming language
    https://www.gnu.org/software/guile/
  133. MIT Scheme
    http://groups.csail.mit.e­du/mac/projects/scheme/
  134. SIOD: Scheme in One Defun
    http://people.delphiforum­s.com/gjc//siod.html
  135. CommonLispForEmacs
    https://www.emacswiki.org/e­macs/CommonLispForEmacs
  136. Elisp: print, princ, prin1, format, message
    http://ergoemacs.org/emac­s/elisp_printing.html
  137. Special Forms in Lisp
    http://www.nhplace.com/ken­t/Papers/Special-Forms.html
  138. Basic Building Blocks in LISP
    https://www.tutorialspoin­t.com/lisp/lisp_basic_syn­tax.htm
  139. Introduction to LISP – University of Pittsburgh
    https://people.cs.pitt.edu/~mi­los/courses/cs2740/Lectures/Lis­pTutorial.pdf
  140. Why don't people use LISP
    https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/
  141. Structured program theorem
    https://en.wikipedia.org/wi­ki/Structured_program_the­orem
  142. Clojure: API Documentation
    https://clojure.org/api/api
  143. Tutorial for the Common Lisp Loop Macro
    http://www.ai.sri.com/pkarp/loop.html
  144. Common Lisp's Loop Macro Examples for Beginners
    http://www.unixuser.org/~e­uske/doc/cl/loop.html
  145. A modern list api for Emacs. No 'cl required.
    https://github.com/magnars/dash.el
  146. The LOOP Facility
    http://www.lispworks.com/do­cumentation/HyperSpec/Body/06_a­.htm
  147. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  148. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  149. Clojure.org: Atoms
    http://clojure.org/Atoms
  150. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  151. Transient Data Structureshttp://clojure.or­g/transients
  152. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  153. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  154. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  155. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  156. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  157. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  158. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  159. 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/
  160. Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
    https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/
  161. Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
    https://www.root.cz/clanky/pro­gramovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/

Autor článku

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