Programovací jazyk Lua patří mezi typické představitele dynamicky typovaných jazyků. V první řadě to znamená, že typy jsou kontrolovány (až) v době běhu programu, tedy v runtime (což znamená, že i případné chyby jsou hlášeny až v runtime). Navíc nejsou typy přiřazené proměnným či parametrům funkcí, ale přímo hodnotám. To mj. také znamená, že se při vytváření proměnných neuvádí jejich typ, protože interpret či just in time překladač použitý datový typ rozpozná přímo z toho, jakým způsobem je hodnota proměnné zapsána.
Co se dozvíte v článku
- Statické a dynamické typové systémy
- Statické typové kontroly
- Dynamické typové kontroly
- Postupný přechod od jazyků s dynamickým typovým systémem k jazykům s volitelnými typy
- Statické typové kontroly implementované v projektu Luau
- Praktická část: překlad projektu Luau
- Malá odbočka: Lua vs Luau z pohledu spotřeby zdrojů (resources)
- Základní datové typy použité v Luau
- Přečtení informace o typu v čase běhu programu (runtime)
- Přidání typových anotací do zdrojového kódu, statická typová kontrola
- Typ any
- Typová inference a řízení „přísnosti“ statické typové kontroly
- Základní typové anotace funkcí
- Typové anotace tabulek
- Tabulka s prvky indexovanými přirozenými čísly
- Tabulka použitá ve funkci slovníku
- Články a seriály o jazyku Lua a souvisejících technologiích na Rootu
- Repositář s demonstračními příklady
- Odkazy na Internetu
Pokud je místo konstantní hodnoty použit výraz či volání funkce, vyhodnocuje se typ výsledku výrazu či výsledku volání funkce, ale až v době návratu z takové funkce. Takto pojatý typový systém přináší mnohé výhody. Především je (alespoň zpočátku) velmi jednoduché ho používat a mnoho uživatelů, kteří takový jazyk používají, vlastně ani nemusí o typech příliš přemýšlet (vše je přece „logické“).
Na druhou stranu se však mohou do vyvíjených programů zanášet různé nepříjemné chyby a taktéž explicitně zapsané informace o typech mohou – pochopitelně pokud jsou použity – do značné míry nahradit dokumentaci (tedy například samotná signatura funkce je mnohdy více popisná, než nějaký někdy i zastaralý komentář). Proto není divu, že u prakticky všech původně dynamicky typovaných jazyků, které dosáhly určité úrovně rozšíření (a nasazení do větších projektů) můžeme vidět přechod či rozšíření o možnost zápisu typových informací.
Obrázek 1: Logo programovacího jazyka Lua (neanimovaná varianta).
To je mnohdy nepovinné rozšíření jazyka; u takových jazyků se potom (i když poněkud nepřesně) mluví o graduálním typovém systému. Jedná se o kombinaci původního dynamického typového systému se systémem statickým, což umožňuje i postupný přechod resp. postupný refaktoring původního zdrojového kódu do jeho striktnější podoby. Dnes se seznámíme s projektem Luau, který (kromě dalších rozšíření) přináší graduální typový systém i do programovacího jazyka Lua.
Obrázek 2: Logo programovacího jazyka Luau.
Statické a dynamické typové systémy
Prakticky každý programovací jazyk, ať se jedná o interpret nebo o překladač, musí v nějaké fázi zpracování programového kódu provádět typové kontroly, tj. zjišťovat, zda se nějaká operace provádí s hodnotami očekávaných typů (tedy například že operace podílu se provádí nad dvojicí čísel a nikoli nad dvěma řetězci nebo dokonce nad tabulkou o hodnotou False).
K této kontrole může docházet buď při překladu (kompilaci) popř. s využitím specializovaného nástroje pro statickou typovou analýzu (a to bez spuštění programového kódu). Takové kontrole datových typů říkáme statická (typová) kontrola. V tomto případě jsou u většiny v současnosti používaných typových systémů datové typy přiřazeny k proměnným či parametrům funkcí a metod, popř. jsou v případě potřeby automaticky odvozovány (typová inference). Ovšem kontrolu typů je alternativně možné provádět i za běhu programu, tedy v runtime. Taková kontrola se nazývá dynamická (typová) kontrola a zajímavá je tím, že typy jsou v tomto případě většinou přiřazeny hodnotám a nikoli proměnným (ovšem záleží na konkrétním programovacím jazyku – i zde existují určité rozdíly).
Statické typové kontroly
Myšlenka, na níž stojí technologie statických typových kontrol, je pro vývojáře většinou velmi snadno pochopitelná, protože se do značné míry podobá dalším analýzám programového (zdrojového) kódu, které provádí interpret (nebo překladač), dále různé lintery, generátory dokumentace, transpilery atd. Celá myšlenka je založena na tom, že u každé proměnné deklarované ve zdrojovém kódu, u každého parametru funkce resp. metody a taktéž u každého návratového parametru funkce či metody se přímo či nepřímo uvede datový typ (nepřímo v případě, že příslušný nástroj umí typ odvodit z použité hodnoty – jedná se, jak jsme si ostatně již naznačili v předchozí kapitole, o takzvanou typovou inferenci).
Příkladem může být klasické céčko, které podporuje statické zápisy typů:
#include <stdio.h>
int main(void) {
static int x = 10000;
static double y = 1.2345;
static double z = y * x;
printf("%d x %f = %f\n", x, y, z);
return 0;
}
Nově je též možné využít (relativně primitivní) podobu typové inference, která je opět prováděna analýzou zdrojového kódu v době překladu, tedy staticky:
#include <stdio.h>
int main(void) {
auto x = 10000;
auto y = 1.2345;
auto z = y * x;
printf("%d x %f = %f\n", x, y, z);
return 0;
}
Díky tomu, že je specifikace typu proměnné, parametru funkce/metody či návratové hodnoty dostupná přímo ve formě zdrojového kódu, může být typová kontrola skutečně statická – nevyžaduje tedy, aby se program spustil. Toto řešení má své nesporné výhody, protože takto specifikované informace o typech dokáží zpracovat i moderní (a nejenom moderní) integrovaná vývojová prostředí či agenti, kteří ji mohou použít v kontextové nápovědě, při kontrole korektnosti zdrojového kódu atd.
Ovšem současně při použití statické typové kontroly narážíme na značnou nevýhodu: je totiž velmi složité vytvořit snadno použitelný a současně i staticky typovaný programovací jazyk (dokonce je možné říci, že se nám to v oboru IT ještě nepodařilo, i když osobně mám slabost pro jazyky z rodiny ML). A další nevýhodou je, že zápis datových typů je vyžadován i v případě, kdy se tvoří pouze jednoduché skripty nebo prototypy. Proto není divu, že mnoho programovacích jazyků (a nutno říci, že mnohdy velmi úspěšných jazyků) striktní zápis datových typů nevyžaduje a tím pádem nebude (zcela) dostupná statická typová kontrola.
U staticky typovaných programovacích jazyků většinou provádí základní typové kontroly už samotný překladač či interpret (to ale není případ programovacího jazyka Lua a dokonce ani interpretru Luau! – viz další kapitoly). Ovšem jak sami uvidíme v navazujícím textu, v závislosti na použitém typovém systému (různé typy variance atd.) se může stát, že některé typové kontroly musí být přesunuty z času překladu (tedy z compile time) do času běhu aplikace (runtime).
Dynamické typové kontroly
Zatímco statické typové kontroly jsou prováděny pouze na základě statické analýzy zdrojových kódů, tj. bez nutnosti (překladu) a spuštění analyzované aplikace, provádí se kontroly dynamické přímo za běhu aplikace. Rozdílem od statických kontrol je, že typy jsou přiřazeny k hodnotám a nikoli k proměnným či parametrům funkcí a metod. Samotný runtime programovacího jazyka (což může být interpret) v takovém případě při každé operaci s hodnotami testuje, jestli mají hodnoty předávané do této operace korektní typy a typ se odvodí (a zapamatuje) i na základě výsledku operace. Mezi takové jazyky patří jak Lua, tak i například Python či JavaScript.
Tento článek je zaměřen na jazyk Lua, takže si uveďme příklad triviální typové chyby – nelze sečíst řetězec s číslem (alespoň ne v Lue – což je ostatně moc dobře):
local foo = "foo" local bar = 1 local baz = foo + bar print(baz)
V případě, že se odhalí nekorektní použití (kombinace) typů, je běh aplikace přerušen: vznikne běhová chyba (například se vyhodí výjimka atd.). To je poměrně podstatný rozdíl oproti statické typové kontrole, která odhalí (či by alespoň měla odhalit) tyto problémy ještě předtím, než se aplikace spustí:
$ lua foobar_nocheck.lua
lua: foobar_nocheck.lua:5: attempt to add a 'string' with a 'number'
stack traceback:
[C]: in metamethod 'add'
foobar_nocheck.lua:5: in main chunk
[C]: in ?
Postupný přechod od jazyků s dynamickým typovým systémem k jazykům s volitelnými typy
V IT se používalo nebo doposud používá několik desítek, velmi pravděpodobně však až několik set programovacích jazyků, z nichž prakticky každý je založen na odlišném typovém systému. Podívejme se tedy pouze na seznam těch programovacích jazyků, které jsou v současnosti nejpopulárnější nebo nejpoužívanější (což ovšem nemusí být totéž). V tomto seznamu nalezneme jak typické zástupce dynamicky typovaných jazyků, tak i programovací jazyky se statickým typovým systémem:
| Dynamicky typované jazyky | Statický typované jazyky |
|---|---|
| Python | C |
| JavaScript | C++ |
| Lua | C# |
| Ruby | Go |
| Perl | Rust |
| Matlab | Java |
| PHP | Scala |
| Groovy | OCaml |
Užitečné bude podrobněji prozkoumat první čtyři výše zmíněné dynamicky typované programovací jazyky, tedy konkrétně programovací jazyk Python, JavaScript, Ruby a pochopitelně i jazyk Lua. Tyto programovací jazyky se původně používaly většinou na tvorbu krátkých skriptů (v případě JavaScriptu běžící v rámci stránky ve webovém prohlížeči – první JavaScriptem oživené stránky používaly jen několikařádkové skripty), ovšem postupně se rozšířily i do mnoha dalších oblastí, takže v nich začaly vznikat mnohdy i velmi rozsáhlé aplikace; v případě jazyka Lua pak relativně rozsáhlé skripty v těch aplikacích, které jsou díky podpoře interpretru jazyka Lua rozšiřitelné o další naprogramovanou funkcionalitu.
A právě u rozsáhlejších aplikací se začal ukazovat dosti velký význam staticky zapisovaných a taktéž staticky kontrolovaných datových typů, protože právě tyto nově zapsané informace umožňují lépe zajistit korektnost rozsáhlých projektů, což se bez použití typových informací do jisté míry nahrazuje větším množstvím jednotkových testů, se všemi z toho plynoucími nevýhodami (o „úroveň níž“ jsou pak jednotkové testy vygenerované z kódu s využitím AI). Navíc uvedení typových informací zlepšuje funkci integrovaných vývojových prostředí a mnohdy umožní se obejít bez podrobnějších komentářů v kódu (jedná se o komentáře typu „v parametru a se jako argument předává celé číslo…“).
Primárně právě z důvodu podpory vývoje rozsáhlejších aplikací pro původně dynamicky typované programovací jazyky postupně vznikla rozšíření, která do nich přidává volitelný či graduální zápis datových typů. A tato rozšíření umožňují statickou kontrolu datových typů s využitím k tomu určených nástrojů:
| Původní jazyk | Rozšíření o staticky kontrolované datové typy |
|---|---|
| JavaScript | TypeScript, Flow |
| Python | Mypy, Pyright, Pyre |
| Ruby | Sorbet |
| Lua | Luau |
Statické typové kontroly implementované v projektu Luau
Luau je projekt, který rozšiřuje možnosti programovacího jazyka Lua o další programové konstrukce a především o podporu zápisu typových informací. Na druhou stranu se autoři snaží o to, aby byl Luau do značné míry kompatibilní s původním jazykem Lua (což ovšem není vždy na sto procent dodrženo, zejména v těch oblastech, v nichž autoři Luau nesouhlasí se směřováním původního jazyka Lua). Luau dokáže provádět statickou typovou kontrolu a pochopitelně jsou prováděny kontroly typů i v runtime.
Typový systém implementovaný v Luau se sice na první pohled podobá systému typových informací zavedených v Pythonu (Mypy atd.), ovšem ve skutečnosti se tyto typové systémy dosti odlišují. Luau podporuje sloučení datových typů (union, což vlastně znamená, že podporuje i volitelné hodnoty (optional) či dokonce průnik datových typů (intersection). Od datového systému Pythonu se Luau odlišuje především ve způsobu definice datového typu tabulek; což ovšem nemusí být překvapující, protože tabulky se v jazyku Lua používají v mnoha oblastech, dokonce i pro objektově orientované programování.
Praktická část: překlad projektu Luau
V praktické části dnešního článku si ukážeme způsob provádění statických typových kontrol realizovaných projektem Luau. Nejdříve je pochopitelně nutné si tento projekt nainstalovat. Na adrese jsou k dispozici již přeložené binární soubory se všemi nástroji, ovšem jsme vývojáři a navíc na Rootu, takže raději provedeme překlad celého projektu Luau ze zdrojových kódů. Luau je z velké části naprogramován v C++, takže je vyžadován překladač tohoto programovacího jazyka a taktéž nástroj make (a vlastně ještě git).
Začneme naklonováním repositáře s projektem Luau:
$ git clone git@github.com:luau-lang/luau.git Cloning into 'luau'... remote: Enumerating objects: 27613, done. remote: Counting objects: 100% (496/496), done. remote: Compressing objects: 100% (292/292), done. remote: Total 27613 (delta 350), reused 204 (delta 204), pack-reused 27117 (from 3) Receiving objects: 100% (27613/27613), 26.40 MiB | 3.62 MiB/s, done. Resolving deltas: 100% (19687/19687), done.
Dále přejdeme do adresáře s naklonovaným projektem a necháme si přeložit nástroje luau a luau-analyze:
$ make config=release luau luau-analyze ... ... ... g++ build/release/CLI/src/FileUtils.cpp.o build/release/CLI/src/Flags.cpp.o build/release/CLI/src/Analyze.cpp.o build/release/CLI/src/AnalyzeRequirer.cpp.o build/release/CLI/src/VfsNavigator.cpp.o build/release/libluauanalysis.a build/release/libluauast.a build/release/libluaucompiler.a build/release/libluaubytecode.a build/release/libluauvm.a build/release/libluaurequire.a build/release/libluauconfig.a build/release/libluaucommon.a -lpthread -o build/release/luau-analyze ln -fs build/release/luau-analyze luau-analyze
Po několika desítkách sekund by měl být překlad dokončen a v adresáři s projektem by měly existovat dva odkazy nazvané luau a luau-analyze:
$ ls -l luau* lrwxrwxrwx. 1 ptisnovs ptisnovs 18 Aug 12 21:30 luau -> build/release/luau lrwxrwxrwx. 1 ptisnovs ptisnovs 26 Aug 12 21:31 luau-analyze -> build/release/luau-analyze
Zajímavé bude zjistit jejich velikost. Jedná se totiž o pořádné „bumbrlíčky“:
$ ls -lLh luau* -rwxr-xr-x. 1 ptisnovs ptisnovs 30M Aug 12 21:30 luau -rwxr-xr-x. 1 ptisnovs ptisnovs 116M Aug 12 21:31 luau-analyze
Po odstranění symbolů příkazem strip bude jejich velikost mnohem přijatelnější:
-rwxr-xr-x. 1 ptisnovs ptisnovs 2.2M Aug 15 15:26 luau -rwxr-xr-x. 1 ptisnovs ptisnovs 4.7M Aug 15 15:26 luau-analyze
V posledním kroku provedeme ověření, že jak luau, tak i luau-analyze, jsou spustitelné:
$ ./luau -h Usage: ./luau [options] [file list] [-a] [arg list] When file list is omitted, an interactive REPL is started instead. Available options: --coverage: collect code coverage while running the code and output results to coverage.out --counters: collect native counters data while running the code and output results to callgrind.out -h, --help: Display this usage message. -i, --interactive: Run an interactive REPL after executing the last script specified. -O<n>: compile with optimization level n (default 1, n should be between 0 and 2). -g<n>: compile with debug level n (default 1, n should be between 0 and 2). --profile[=N]: profile the code using N Hz sampling (default 10000) and output results to profile.out --timetrace: record compiler time tracing information into trace.json --codegen: execute code using native code generation --codegen-cold: execute code using native code generation, including any functions deemed not profitable to natively compile --codegen-perf: execute code using native code generation and profile using perf (only on Linux) --program-args,-a: declare start of arguments to be passed to the Luau program --fflags=: comma-separated list of fast flags to enable/disable (--fflags=true,false,LuauFlag1=true,LuauFlag2=false). --jit-inliner: enable JIT bytecode inliner
a:
$ ./luau-analyze --help
Usage: ./luau-analyze [--mode] [options] [file list]
Available modes:
omitted: typecheck and lint input files
--annotate: typecheck input files and output source with type annotations
Available options:
--formatter=plain: report analysis errors in Luacheck-compatible format
--formatter=gnu: report analysis errors in GNU-compatible format
--mode=strict: default to strict mode when typechecking
--solver={new|old}: selects which typechecker to use (defaults to the new solver)
--timetrace: record compiler time tracing information into trace.json
Malá odbočka: Lua vs Luau z pohledu spotřeby zdrojů (resources)
Jak již bylo naznačeno v předchozí kapitole, je Luau poměrně velkým „bumbrlíčkem“, což je v ostrém kontrastu s původním interpretrem jazyka Lua. Adresář s naklonovaným projektem Luau po překladu zabírá více než gigabajt:
$ du -h 1.1G .
Také čas překladu je poměrně dlouhý:
$ time make config=release luau luau-analyze real 2m46.206s user 18m22.645s sys 0m56.697s
V porovnání s tím je překlad jazyka Lua prakticky okamžitý (následující sekvence příkazů provede překlad interpretru Lua i překladače do bajtkódu):
$ wget https://www.lua.org/ftp/lua-5.5.1.tar.gz
Saving 'lua-5.5.1.tar.gz'
HTTP response 200 OK [https://www.lua.org/ftp/lua-5.5.1.tar.gz]
lua-5.5.1.tar.gz 100% [=====================================================================================>] 389.29K --.-KB/s
[Files: 1 Bytes: 389.29K [421.31KB/s] Redirects: 0 Todo: 0 Errors: 0 ]
$ tar xvfz lua-5.5.1.tar.gz
lua-5.5.1/
lua-5.5.1/Makefile
lua-5.5.1/doc/
lua-5.5.1/doc/OSIApproved.png
lua-5.5.1/doc/luac.1
lua-5.5.1/doc/manual.html
lua-5.5.1/doc/manual.css
lua-5.5.1/doc/contents.html
lua-5.5.1/doc/logo.png
$ time make all
make[1]: Entering directory '/tmp/ramdisk/lua-5.5.1/src'
Guessing Linux
ar rcu liblua.a lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
ranlib liblua.a
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_USE_LINUX -c -o lua.o lua.c
gcc -std=gnu99 -o lua lua.o liblua.a -lm -Wl,-E -ldl
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_USE_LINUX -c -o luac.o luac.c
gcc -std=gnu99 -o luac luac.o liblua.a -lm -Wl,-E -ldl
make[3]: Leaving directory '/tmp/ramdisk/lua-5.5.1/src'
make[2]: Leaving directory '/tmp/ramdisk/lua-5.5.1/src'
make[1]: Leaving directory '/tmp/ramdisk/lua-5.5.1/src'
real 0m9.716s
user 0m8.823s
sys 0m0.669s
Reálné časy překladu z pohledu uživatele mluví jasně: 2m46s vs. necelých deset sekund.
Navíc jsou mnohem menší i výsledné binární soubory (interpret a překladač do bajtkódu):
$ ls -lh src/lua src/luac -rwxr-xr-x. 1 ptisnovs ptisnovs 310K Aug 14 17:53 src/lua -rwxr-xr-x. 1 ptisnovs ptisnovs 227K Aug 14 17:53 src/luac
$ ls -lLh /usr/bin/lua* -rwxr-xr-x. 1 root root 20K Nov 11 2025 /usr/bin/lua -rwxr-xr-x. 1 root root 159K Nov 11 2025 /usr/bin/luac $ ls -lLh /usr/lib64/liblua* -rwxr-xr-x. 1 root root 281K Nov 11 2025 /usr/lib64/liblua-5.4.so
Pro úplnost ještě můžeme zjistit velikosti spustitelných souborů a knihoven využívaných LuaJITem, což je ovšem oproti původnímu interpretru mnohem sofistikovanější projekt:
-rwxr-xr-x. 1 root root 606K Nov 4 2025 /usr/bin/luajit -rwxr-xr-x. 1 root root 606K Nov 4 2025 /usr/bin/luajit-2.1.1761727121 -rwxr-xr-x. 1 root root 589K Nov 4 2025 /usr/lib64/libluajit-5.1.so.2 -rwxr-xr-x. 1 root root 589K Nov 4 2025 /usr/lib64/libluajit-5.1.so.2.1.1761727121
Základní datové typy použité v Luau
V programovacím jazyku Luau, který vznikl rozšířením jazyka Lua, je teoreticky možné využívat deset základních datových typů vypsaných v tabulce níže. Prvních osm datových typů má stejný význam, jako datové typy z původní Luy. Přidány byly typy vector a buffer. vector ovšem musí být implementován v například v programu, který Luau používá jako vestavěný jazyk. V případě Robloxu se namísto tohoto typu použije Vector3 (už zde můžeme vidět potenciální problémy). Typ buffer je implementován i v základní variantě Luau, kterou jsme si přeložili v rámci úvodní části dnešního článku.
Samotný název datového typu (první sloupec tabulky) tedy není rezervovaným ani klíčovým slovem, naopak některé hodnoty určitých datových typů (především nil, true, false) klíčovými hodnotami jsou. Určitou výjimkou je první datový typ, který je pojmenovaný stejně jako (jediná a v celém programu jedinečná) hodnota tohoto typu: nil.
| # | Datový typ | Význam |
|---|---|---|
| 1 | nil | datový typ hodnoty nil, která je v celém programu jedinečná; tato hodnota se definitoricky odlišuje od všech ostatních hodnot (nutná podmínka pro správné porovnávání); některými vlastnostmi se podobá céčkovému NULL či Javovskému null |
| 2 | boolean | datový typ pravdivostních hodnot true a false |
| 3 | number | reálné číslo podle IEEE 754 s dvojnásobnou přesností (v céčku či Javě se jedná o typ double); při překladu jazyka Lua či Luau je však možné tento typ změnit, například na celá čísla |
| 4 | string | řetězec |
| 5 | function | funkce, buď vytvořená přímo ve skriptu či zaregistrovaná céčková funkce; s funkcemi lze manipulovat podobně jako s dalšími datovými typy, včetně jejich přiřazení do proměnných, uvedení funkce jako parametru jiné funkce atd. |
| 6 | userdata | data vytvořená v hostitelském (céčkovém) programu; v samotném skriptu je jejich použití omezeno (interpretr nemá dostatek informací o struktuře skutečně uložených dat) |
| 7 | thread | vlákno použité pro implementaci koprogramů (coroutine) |
| 8 | table | asociativní pole – velmi flexibilní datový typ, na kterém je založena většina funkcionality jazyka Lua, včetně objektového systému (v asociativním poli mohou být umístěny mj. i funkce) |
| 9 | vector | numerický vektor se dvěma, třemi nebo čtyřmi složkami |
| 10 | buffer | struktura obsahující sekvenci bajtů používaná pro serializaci a deserializaci |
local b = buffer.create(8) buffer.writeu8(b, 0, 42) buffer.writeu8(b, 1, 65) buffer.writeu8(b, 2, 66) buffer.writeu8(b, 3, 67) buffer.writeu8(b, 5, 42) print(buffer.tostring(b))
Výsledek, který tento skript vypíše, by měl vypadat takto:
$ luau simple_buffer.lua *ABC*
Přečtení informace o typu v čase běhu programu (runtime)
Informaci o typu lze, podobně jako v případě klasického (původního) jazyka Lua, získat v čase běhu programu (runtime) s využitím vestavěné funkce type, která vrací řetězec se jménem typu. V dalším příkladu pracujeme se všemi typy z výše uvedené tabulky kromě vektorů:
v1 = nil
v2 = true
v3 = 123
v4 = 0.5
v5 = "hello"
v6 = {1, 2, 3}
v7 = function() end
v8 = coroutine.create(function() end)
v9 = newproxy and newproxy(true) or nil
v10 = buffer and buffer.create(1) or nil
print("type(v1)=", type(v1))
print("type(v2)=", type(v2))
print("type(v3)=", type(v3))
print("type(v4)=", type(v4))
print("type(v5)=", type(v5))
print("type(v6)=", type(v6))
print("type(v7)=", type(v7))
print("type(v8)=", type(v8))
print("type(v9)=", type(v9))
print("type(v10)=", type(v10))
Výsledky vypsané na standardní výstup po spuštění tohoto skriptu by neměly být nijak překvapující:
$ ./luau runtime_types.lua type(v1)= nil type(v2)= boolean type(v3)= number type(v4)= number type(v5)= string type(v6)= table type(v7)= function type(v8)= thread type(v9)= userdata type(v10)= buffer
Přidání typových anotací do zdrojového kódu, statická typová kontrola
Nejdůležitějším rozšířením syntaxe a sémantiky, které bylo implementováno v projektu Luau, je možnost zápisu typových anotací při deklaraci proměnných popř. funkcí (či metod). Začněme typovými anotacemi použitými při deklaraci proměnných. Typ proměnné se zapisuje za název proměnné a je od ní oddělen dvojtečkou:
--!strict local foo : number = 6 local bar : number = 7 local baz : number = foo * bar print(baz)
Statickou typovou kontrolu provádí nástroj nazvaný luau-analyze. S tímto nástrojem souvisí i první řádek (komentář) ve skriptu, který vyžaduje striktní typové kontroly. Zkusme si tedy ověřit, zda je výše uvedený skript korektní, minimálně v souvislosti s použitými datovými typy. Pokud luau-analyze nevypíše žádnou chybu, je vše v pořádku:
$ ./luau-analyze type_annotations_1.lua
V dalším demonstračním příkladu se pokusme vynásobit numerickou hodnotu a řetězec. Použijeme ovšem korektní typy obou proměnných; nekorektní je až operace, kterou s proměnnými budeme chtít provést:
--!strict local foo : number = 6 local bar : string = "foo" local baz : number = foo * bar print(baz)
Statická typová kontrola tento problém odhalí, a to bez nutnosti spouštění programu (který se navíc při testech vůbec nemusí do této části kódu dostat):
$ ./luau-analyze type_annotations_2.lua ./type_annotations_2.lua(5,22): TypeError: Operator '*' could not be applied to operands of types number and string; there is no corresponding overload for __mul
Dokážeme ovšem systém typové kontroly obelstít tak, že u všech proměnných vynutíme typ number?
--!strict local foo : number = 6 local bar : number = "foo" local baz : number = foo * bar print(baz)
I tento problém statická typová kontrola odhalí, tentokrát ještě před pokusem o vynásobení hodnot:
$ ./luau-analyze type_annotations_3.lua ./type_annotations_3.lua(4,22): TypeError: Expected this to be 'number', but got 'string'
Typ any
V jazyku Luau je podporován i typ any, který znamená, že příslušná proměnná či parametr může obsahovat libovolnou hodnotu. Jedná se tedy o množinu všech definovaných typů. To ovšem způsobuje určité problémy při statické typové kontrole a proto se na tento typ zaměříme v této kapitole.
Základní způsob použití je na první pohled jednoduchý – v deklaraci proměnné použijeme typ any, který znamená, že proměnné bude možné přiřadit (později) hodnotu jakéhokoli typu:
--!strict local foo : any = 6 local bar : any = 7 local baz : any = foo * bar print(baz)
Tento program bude (v runtime) pracovat korektně a ani statická typová kontrola neodhalí žádné problémy:
$ ./luau any_type_1.lua 42 $ ./luau-analyze any_type_1.lua
V další variantě příkladu se pokusíme o vynásobení dvou hodnot, které mají v čase běhu nekompatibilní typy:
--!strict local foo : any = 6 local bar : any = "foo" local baz : any = foo * bar print(baz)
A právě zde narazíme na problém – statická typová kontrola tuto chybu neodhalí a program je (zdánlivě) bezchybný:
$ ./luau-analyze any_type_2.lua
Ovšem – naprosto podle všech předpokladů – dojde k běhové chybě:
$ ./luau any_type_2.lua ./any_type_2.lua:5: attempt to perform arithmetic (mul) on number and string stacktrace: ./any_type_2.lua:5
$ cat any_type.py
from typing import Any
foo : Any = 6
bar : Any = "foo"
baz : Any = foo + bar
print(baz)
$ mypy --strict any_type.py
Success: no issues found in 1 source file
$ python any_type.py
Traceback (most recent call last):
File "/home/ptisnovs/temp/ramdisk/luau-examples/any_type.py", line 5, in <module>
baz : Any = foo + bar
~~~~^~~~~
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Informace o typu hodnoty je stále dostupná v čase běhu, nezávisle na deklaraci typu proměnné:
--!strict local foo : any = 6 local bar : any = 7 local baz : any = foo * bar print(type(foo)) print(type(bar)) print(type(baz))
Tento program nevypíše třikrát any, ale třikrát number:
$ luau any_type_3.lua number number number
Typová inference a řízení „přísnosti“ statické typové kontroly
Při provádění statických typových kontrol dokáže nástroj luau-analyze do určité míry odvozovat datové typy proměnných, parametrů funkcí i návratových hodnot funkcí. Navíc je možné řídit, do jaké míry budou statické typové kontroly striktní. Obě zmíněné vlastnosti si popíšeme v této kapitole.
Začneme skriptem, ve kterém nejsou typové anotace použity. Pouze jsou v něm inicializovány tři proměnné:
local foo = "foo" local bar = 1 local baz = foo + bar print(baz)
Při pokusu o interpretaci tohoto skriptu dojde (podle všech očekávání) k běhové chybě, protože v jazyce Lua není možné sečíst řetězec (proměnnou obsahující řetězec) s celým číslem (proměnnou obsahující celé číslo):
$ ./luau foobar_nocheck.lua ./foobar_nocheck.lua:5: attempt to perform arithmetic (add) on string and number stacktrace: ./foobar_nocheck.lua:5
Tuto chybu je možné díky typovým inferencím odhalit statickou typovou kontrolou nástrojem luau-analyzer:
$ ./luau-analyze foobar.lua ./foobar.lua(5,13): TypeError: Operator '+' could not be applied to operands of types string and number; there is no corresponding overload for __add
Z chybového hlášení je zřejmé, že byly korektně odvozeny typy proměnných foo i bar, protože jména jejich typů se objevují v popisu chyby. Ovšem ne vždy je vhodné se spolehnout na odvození typů – jednou z výhod typového systému je totiž i to, že zápis typů slouží i jako dokumentace.
To, jak striktní bude statická typová kontrola, se řídí přepínačem -strict, nebo (což je většinou lepší) speciálně strukturovanými komentáři, které se typicky zapisují na začátek zdrojových kódů.
Komentářem --!nocheck je možné statickou typovou kontrolu vypnout:
--!nocheck local foo = "foo" local bar = 1 local baz = foo + bar print(baz)
Chyba s nekompatibilním součtem nebude v tomto případě odhalena:
$ ./luau-analyze foobar_nocheck.lua
Taktéž je možné použít „nestriktní“ režim statické typové konroly. Typová inference je v tomto případě provedena jen do určité míry, což vede k tomu, že se většinou odvodí, že proměnná je typu any:
--!nonstrict local foo = "foo" local bar = 1 local baz = foo + bar print(baz)
V tomto skriptu opět nebude žádná chyba nalezena:
$ ./luau-analyze foobar_nostrict.lua
A konečně je možné si nechat vynutit striktní typovou kontrolu:
--!strict local foo = "foo" local bar = 1 local baz = foo + bar print(baz)
Toto je již mnohem užitečnější přepínač, protože statická typová kontrola odhalí chybu, kterou jsem ve skriptu udělal:
$ ./luau-analyze foobar_strict.lua ./foobar_strict.lua(5,13): TypeError: Operator '+' could not be applied to operands of types string and number; there is no corresponding overload for __add
Základní typové anotace funkcí
Specifikace typů proměnných je sice užitečná, ovšem v praxi je možná ještě důležitější mít možnost specifikovat typové anotace funkcí. Konkrétně se jedná o specifikaci typů parametrů funkce a taktéž typů návratové hodnoty nebo návratových hodnot. Podívejme se na jednoduchou funkci provádějící součet dvou numerických hodnot. Specifikace typů parametrů i typu návratové hodnoty se v tomto případě provede následovně:
function add(x:number, y:number):number
return x+y
end
Díky tomu může statická typová kontrola odhalit, že se funkci snažíme zavolat se špatnými typy parametrů:
--!strict
function add(x:number, y:number):number
return x+y
end
add(1, 2)
add("foo", "bar")
add(1, "baz")
add(1)
add(1, 2, 3)
Chyba je v tomto případě odhalena na řádcích 8, 9 a 10, tedy už nikoli na posledním řádku, kde funkci voláme s předáním tří parametrů:
$ ./luau-analyze functions_1.lua ./functions_1.lua(8,5): TypeError: Expected this to be 'number', but got 'string' ./functions_1.lua(8,12): TypeError: Expected this to be 'number', but got 'string' ./functions_1.lua(9,8): TypeError: Expected this to be 'number', but got 'string' ./functions_1.lua(10,1): TypeError: Argument count mismatch. Function expects 2 arguments, but only 1 is specified
V jazyce Lua dokáže funkce vracet větší množství hodnot, tedy dvojice, trojice atd. V takovém případě musíme specifikaci typů návratových hodnot uzavřít do kulatých závorek. Příkladem je funkce, která prohodí hodnoty svých parametrů a vrátí je v opačném pořadí:
--!strict
function swap(x:number, y:number):(number,number)
return y, x
end
print(swap(1, 2))
print(swap("foo", "bar"))
print(swap("foo", 2))
print(swap(1))
print(swap(1, 2, 3))
Opět si ověřme, jak bude vypadat výsledek statické typové kontroly v tomto případě:
$ ./luau-analyze functions_2.lua ./functions_2.lua(8,12): TypeError: Expected this to be 'number', but got 'string' ./functions_2.lua(8,19): TypeError: Expected this to be 'number', but got 'string' ./functions_2.lua(9,12): TypeError: Expected this to be 'number', but got 'string' ./functions_2.lua(10,7): TypeError: Argument count mismatch. Function expects 2 arguments, but only 1 is specified
V navazujícím článku si ukážeme specifikaci typů generických funkcí. Mohlo by se zdát, že vlastně ani nepotřebujeme speciální syntaxi a sémantiku pro generické funkce, protože postačuje použít typ any. To ovšem (prakticky) zcela vypne typové kontroly:
--!strict
function swap(x:any, y:any):(any,any)
return y, x
end
print(swap(1, 2))
print(swap("foo", "bar"))
print(swap("foo", 2))
print(swap(1))
print(swap(1, 2, 3))
Statická typová kontrola nyní neodhalí žádný problém!:
$ ./luau-analyze functions_3.lua
Typové anotace tabulek
Programovací jazyk Lua a následně i Luau nabízí vývojářům jen jediný strukturovaný datový typ. Tím je tabulka, přičemž tabulky je možné využít v mnoha oblastech a různými způsoby:
- Běžné pole s prvky indexovanými od nuly. U takového pole lze snadno zjistit jeho délku, k čemuž slouží specializovaný operátor. Navíc je možné iterovat přes prvky tabulky s využitím funkci/iterátoripairs.
- Slovník, přičemž typ klíčů a hodnot může být prakticky libovolný (s výjimkou hodnoty nil, která může situaci zkomplikovat). I přes prvky slovníku je možné iterovat, tentokrát ovšem s využitím funkce/iterátoru pairs (bez i na začátku).
- Struktura popř. objekt, což je vlastně slovník s klíči typu řetězec, jejichž počet a jména se nemění. Pro přístup k prvkům je možné použít jak zápis slovník[„prvek“], tak i slovník.prvek.
Kvůli tomu, že tabulky vystupují v různých rolích, je poměrně komplikovaný i způsob zápisu jejich typových anotací. K tomu ještě musíme připočíst to, jakým způsobem se zjišťuje, které typy (tabulek) jsou z hlediska typového systému kompatibilní – poslední způsob použití tabulek (implementace struktur a především pak objektů) totiž znamená, že jsme do programovacího jazyka zanesli objektově orientované programování a částečně i jeho vlastnost „potomek může nahradit předka“. To je však téma, kterému se budeme věnovat ve druhém pokračování dnešního článku.
Tabulka s prvky indexovanými přirozenými čísly
Nejjednodušší (i když možná zpočátku špatně čitelná) je typová anotace tabulky, která je využita jako homogenní pole, tj. pole s prvky stejných typů. Příkladem může být funkce sum, která akceptuje pole s numerickými hodnotami (ale ne již například pole řetězců). Typ prvků bude v tomto případě zapsán do složených závorek, tj. {number}. Takové pole může mít neomezený počet prvků:
--!strict
function sum(t: {number}): number
local s = 0
for i = 1, #t do
s = s + t[i]
end
return s
end
local s1 = sum({1,2,3,4})
print(s1)
Statická typová analýza by neměla najít žádný problém:
$ ./luau-analyze table_type_1.lua
Pokusme se nyní do stejně definované funkce předat pole, jehož prvky nebudou typu number, ale například string:
--!strict
function sum(t: {number}): number
local s = 0
for i = 1, #t do
s = s + t[i]
end
return s
end
local s1 = sum({"foo","bar","baz"})
print(s1)
V tomto případě statická typová kontrola odhalí, že se snažíme do funkce sum sice předávat tabulku, ovšem s prvky, jejichž typy nejsou očekávány:
$ ./luau-analyze table_type_2.lua ./table_type_2.lua(11,17): TypeError: Expected this to be 'number', but got 'string' ./table_type_2.lua(11,23): TypeError: Expected this to be 'number', but got 'string' ./table_type_2.lua(11,29): TypeError: Expected this to be 'number', but got 'string'
Tabulka použitá ve funkci slovníku
Podívejme se ještě v rychlosti na způsob použití tabulky, která bude vystupovat v roli slovníku (dictionary). V následujícím demonstračním příkladu je deklarována tabulka (bez uvedení typů) se třemi hodnotami uloženými pod klíči „x“, „y“ a „z“ (klíče jsou řetězci). Poté tabulku vypíšeme a následně se pokusíme o přístup ke čtyřem prvkům (jeden neexistuje):
--!strict
local t = {x=1, y=2, z=3}
for k, v in pairs(t) do
print(k, v)
end
print(t.x)
print(t.y)
print(t.z)
print(t.w)
Tento skript je plně funkční (a to i v původním interpretru jazyka Lua), neboť pokus o čtení neexistujícího prvku vrátí hodnotu nil:
$ lua table_type_3.lua y 2 x 1 z 3 1 2 3 nil
Můžeme však explicitně určit typ všech prvků, které jsou ve slovníku očekávány (každý prvek pochopitelně může být libovolného typu):
--!strict
local t : {x:number, y:number, z:number} = {x=1, y=2, z=3}
print(t.x)
print(t.y)
print(t.z)
print(t.w)
Nyní dokáže statická typová kontrola odhalit, že se snažíme přistoupit k prvku s klíčem „w“, který ovšem není součástí typové specifikace tabulky/slovníku:
$ ./luau-analyze table_type_4.lua
./table_type_4.lua(8,7): TypeError: Key 'w' not found in table '{ x: number, y: number, z: number }'
Na druhou stranu bude tento skript plně funkční; pokud nám tedy nevadí, že se (bez ohlášení chyby) budou vracet hodnoty nil pro neexistující prvky:
$ ./luau table_type_4.lua 1 2 3 nil
V posledním demonstračním příkladu, který si dnes ukážeme, budeme do prvků tabulky/slovníku typu string a bool zapisovat hodnoty nesprávných typů:
--!strict
local t : {x:number, y:string, z:boolean} = {x=1, y=2, z=3}
print(t.x)
print(t.y)
print(t.z)
print(t.w)
Z výsledků statické typové kontroly je patrné, že obě špatná přiřazení jsou odhalena:
$ ./luau-analyze table_type_5.lua
./table_type_5.lua(3,50): TypeError: Expected this to be 'string', but got 'number'
./table_type_5.lua(3,58): TypeError: Expected this to be 'boolean', but got 'number'
./table_type_5.lua(8,7): TypeError: Key 'w' not found in table '{ x: number, y: string, z: *error-type* }'
Články a seriály o jazyku Lua a souvisejících technologiích na Rootu
S programovacím jazykem Lua a popř. jazyky, které Luou nějakým způsobem souvisí (MoonScript) jsme se již na stránkách Roota mnohokrát setkali; a to především ve dvojici seriálů a v několika dalších článcích, které vyšly mimo oba seriály:
- Seriál Programovací jazyk Lua
https://www.root.cz/serialy/programovaci-jazyk-lua/ - Seriál Torch: framework pro strojové učení
https://www.root.cz/serialy/torch-framework-pro-strojove-uceni/ - Skriptovací jazyk Lua v aplikacích naprogramovaných v Go
https://www.root.cz/clanky/skriptovaci-jazyk-lua-v-aplikacich-naprogramovanych-v-go/ - LuaJIT – Just in Time překladač pro programovací jazyk Lua
https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua/ - Použití nástroje RQ (Redis Queue) pro správu úloh zpracovávaných na pozadí
https://www.root.cz/clanky/pouziti-nastroje-rq-redis-queue-pro-spravu-uloh-zpracovavanych-na-pozadi/ - Proudy (streams) podporované systémem Redis
https://www.root.cz/clanky/proudy-streams-podporovane-systemem-redis/ - Proudy (streams) podporované systémem Redis (dokončení)
https://www.root.cz/clanky/proudy-streams-podporovane-systemem-redis-dokonceni/ - Databáze Redis (nejenom) pro vývojáře používající Python
https://www.root.cz/clanky/databaze-redis-nejenom-pro-vyvojare-pouzivajici-python/ - Databáze Redis (nejenom) pro vývojáře používající Python (dokončení)
https://www.root.cz/clanky/databaze-redis-nejenom-pro-vyvojare-pouzivajici-python-dokonceni/ - Použití databáze Redis v aplikacích naprogramovaných v Go
https://www.root.cz/clanky/pouziti-databaze-redis-v-aplikacich-naprogramovanych-v-go/ - Použití databáze Redis v aplikacích naprogramovaných v Go (2)
https://www.root.cz/clanky/pouziti-databaze-redis-v-aplikacich-naprogramovanych-v-go-2/ - Lua: univerzální distribuce programovacího jazyka Lua
https://www.root.cz/clanky/ulua-univerzalni-distribuce-programovaciho-jazyka-lua/ - Lupa: propojení programovacích jazyků Lua a Python
https://www.root.cz/clanky/lupa-propojeni-programovacich-jazyku-lua-a-python/
Repositář s demonstračními příklady
Všechny demonstrační příklady popsané v tomto článku naleznete i v repositáři https://github.com/tisnik/luau-examples. Následují odkazy na jednotlivé příklady:
| # | Příklad | Stručný popis | Adresa příkladu |
|---|---|---|---|
| 1 | runtime_types.lua | tisk typu hodnoty, který je získaný v čase běhu programu (runtime) | https://github.com/tisnik/luau-examples/blob/master/runtime_types.lua |
| 2 | simple_buffer.lua | základní způsob použití bufferu, tj. hodnoty typu buffer | https://github.com/tisnik/luau-examples/blob/master/simple_buffer.lua |
| 3 | type_annotations_1.lua | typové anotace použité při deklaraci proměnných | https://github.com/tisnik/luau-examples/blob/master/type_annotations_1.lua |
| 4 | type_annotations_2.lua | pokus o provedení operace pro nekompatibilní datové typy | https://github.com/tisnik/luau-examples/blob/master/type_annotations_2.lua |
| 5 | type_annotations_3.lua | pokus o inicializaci proměnné hodnotou nekompatibilního typu | https://github.com/tisnik/luau-examples/blob/master/type_annotations_3.lua |
| 6 | any_type_1.lua | typ any | https://github.com/tisnik/luau-examples/blob/master/any_type_1.lua |
| 7 | any_type_2.lua | důkaz, že statická typová kontrola nemusí odhalit všechny problematické operace | https://github.com/tisnik/luau-examples/blob/master/any_type_2.lua |
| 8 | any_type_3.lua | běhové informace o typu vs. statický typ any | https://github.com/tisnik/luau-examples/blob/master/any_type_3.lua |
| 9 | foobar_nocheck.lua | zákaz typových kontrol s využitím strukturovaného komentáře | https://github.com/tisnik/luau-examples/blob/master/foobar_nocheck.lua |
| 10 | foobar_nonstrict.lua | zákaz striktních typových kontrol s využitím strukturovaného komentáře | https://github.com/tisnik/luau-examples/blob/master/foobar_nonstrict.lua |
| 11 | foobar_strict.lua | povolení striktních typových kontrol s využitím strukturovaného komentáře | https://github.com/tisnik/luau-examples/blob/master/foobar_strict.lua |
| 12 | foobar_types.lua | zjištění skutečných typů hodnot v čase běhu programu (runtime) | https://github.com/tisnik/luau-examples/blob/master/foobar_types.lua |
| 13 | functions_1.lua | typové anotace parametrů funkce i její návratové hodnoty | https://github.com/tisnik/luau-examples/blob/master/functions_1.lua |
| 14 | functions_2.lua | typové anotace funkce vracející dvojici hodnot | https://github.com/tisnik/luau-examples/blob/master/functions_2.lua |
| 15 | functions_3.lua | využití typu any při zápisu typové anotace funkce | https://github.com/tisnik/luau-examples/blob/master/functions_3.lua |
| 16 | functions_4.lua | generická funkce: základní způsob zápisu | https://github.com/tisnik/luau-examples/blob/master/functions_4.lua |
| 17 | functions_5.lua | generická funkce: základní způsob zápisu, odlišné skutečné parametry při volání | https://github.com/tisnik/luau-examples/blob/master/functions_5.lua |
| 18 | functions_6.lua | generické funkce a statické typové kontroly | https://github.com/tisnik/luau-examples/blob/master/functions_6.lua |
| 19 | table_type_1.lua | typová anotace tabulky použité jako běžné pole | https://github.com/tisnik/luau-examples/blob/master/table_type_1.lua |
| 20 | table_type_2.lua | předání nekorektního typu tabulky | https://github.com/tisnik/luau-examples/blob/master/table_type_2.lua |
| 21 | table_type_3.lua | tabulka použitá v roli slovníku | https://github.com/tisnik/luau-examples/blob/master/table_type_3.lua |
| 22 | table_type_4.lua | typová anotace tabulky použité v roli slovníku (korektní varianta) | https://github.com/tisnik/luau-examples/blob/master/table_type_4.lua |
| 23 | table_type_5.lua | typová anotace tabulky použité v roli slovníku (nekorektní varianta) | https://github.com/tisnik/luau-examples/blob/master/table_type_5.lua |
| 24 | table_type_6.lua | typová anotace nepovinného prvku tabulky | https://github.com/tisnik/luau-examples/blob/master/table_type_6.lua |
| 25 | table_type_7.lua | definice nového datového typu založeného na tabulce | https://github.com/tisnik/luau-examples/blob/master/table_type_7.lua |
| 26 | table_type_8.lua | typová kompatibilita různých typů založených na tabulkách | https://github.com/tisnik/luau-examples/blob/master/table_type_8.lua |
| 27 | table_type_9.lua | typová kompatibilita různých typů založených na tabulkách | https://github.com/tisnik/luau-examples/blob/master/table_type_9.lua |
Odkazy na Internetu
- Repositář projektu luau
https://github.com/luau-lang/luau - Oficiální vydání
https://github.com/luau-lang/luau - An introduction to Luau
https://luau.org/getting-started/ - Luau Programming Full Course (2026) – For Beginners
https://www.youtube.com/watch?v=9kdRHP-FZCA - Roblox: Luau
https://create.roblox.com/docs/luau - How to start learning luau?
https://devforum.roblox.com/t/how-to-start-learning-luau/3823179 - Luau (programming language)
https://en.wikipedia.org/wiki/Luau_(programming_language) - Gradual typing
https://en.wikipedia.org/wiki/Gradual_typing - Gradual typing in Python
https://www.geeksforgeeks.org/python/gradual-typing-in-python/ - Łukasz Langa: Gradual Typing of Production Applications
https://www.youtube.com/watch?v=zKre4DKAB30 - lupa na GitHubu
https://github.com/scoder/lupa - lupa na PyPi
https://pypi.org/project/lupa/ - Lunatic Python
https://labix.org/lunatic-python - Seriál Programovací jazyk Lua
https://www.root.cz/serialy/programovaci-jazyk-lua/ - Python na Rootu
https://www.root.cz/n/python/ - Lua na Wikipedii
https://en.wikipedia.org/wiki/Lua - Python (programming language) na Wikipedii
https://en.wikipedia.org/wiki/Python_(programming_language) - Languages that compile to Lua
https://github.com/hengestone/lua-languages?tab=readme-ov-file#languages-that-compile-to-lua - Repositář projektu Lua Fun
https://github.com/luafun/luafun - The Lua VM, on the Web
https://kripken.github.io/lua.vm.js/lua.vm.js.html - Lua.vm.js REPL
https://kripken.github.io/lua.vm.js/repl.html - lua2js
https://www.npmjs.com/package/lua2js - lua2js na GitHubu
https://github.com/basicer/lua2js-dist - Lambda the Ultimate: Coroutines in Lua,
http://lambda-the-ultimate.org/node/438 - Coroutines Tutorial,
http://lua-users.org/wiki/CoroutinesTutorial - Lua Coroutines Versus Python Generators,
http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators - ULua: Universal Lua Distribution
https://ulua.io/index.html - deque
https://github.com/catwell/cw-lua/blob/master/deque/deque.test.lua - Repositář projektu Lua Fun
https://github.com/luafun/luafun - Lua Functional 0.1.3 documentation
https://luafun.github.io/reference.html - Lua Functional: Getting Started
https://luafun.github.io/getting_started.html - LuaRocks
https://luarocks.org/ - Documentation
Dokumentace ke správci balíčků LuaRocks - Rockspec knihovny Fun
https://raw.githubusercontent.com/luafun/luafun/master/fun-scm-1.rockspec - Awesome Lua – A curated list of quality Lua packages and resources.
https://github.com/LewisJEllis/awesome-lua - LuaJIT
https://luajit.org/ - Running LuaJIT
https://luajit.org/running.html - LuaJIT na GitHubu
https://github.com/luajit - Repositář projektu Moses
https://github.com/Yonaba/Moses/ - Návod k použití knihovny Moses
https://github.com/Yonaba/Moses/blob/master/doc/tutorial.md - Lambda the Ultimate: Coroutines in Lua
http://lambda-the-ultimate.org/node/438 - Coroutines Tutorial
http://lua-users.org/wiki/CoroutinesTutorial - Lua Coroutines Versus Python Generators
http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators - Programming in Lua 9.1 – Coroutine Basics
http://www.lua.org/pil/9.1.html - How to understand clojure's lazy-seq
https://stackoverflow.com/questions/44095400/how-to-understand-clojures-lazy-seq - Lua Implementations
http://lua-users.org/wiki/LuaImplementations - Generator (computer programming)
https://en.wikipedia.org/wiki/Generator_(computer_programming) - Lua 5.2 Reference Manual: 6.10 – The Debug Library
http://www.lua.org/manual/5.2/manual.html#6.10 - Issues k projektu LuaFun
https://github.com/luafun/luafun/issues - Archived | Embed Lua for scriptable apps
https://developer.ibm.com/tutorials/l-embed-lua/ - Embedding Lua
https://www.oreilly.com/library/view/lua-quick-start/9781789343229/3a6f3daf-f74c-4a25-a125–584da58568e4.xhtml - PDM: moderní správce balíčků a virtuálních prostředí Pythonu
https://www.root.cz/clanky/pdm-moderni-spravce-balicku-a-virtualnich-prostredi-pythonu/ - Which Programming Languages Index by One?
https://therenegadecoder.com/code/which-programming-languages-index-by-one/ - 2Zero-based indexing considered harmful
https://craftofcoding.wordpress.com/2021/03/26/zero-based-indexing-considered-harmful/ - Lua downloads
https://www.lua.org/download.html - An introduction to Luau types
https://luau.org/types/ - PEP 585 – Type Hinting Generics In Standard Collections
https://peps.python.org/pep-0585/ - mypy homepage
https://www.mypy-lang.org/ - mypy documentation
https://mypy.readthedocs.io/en/stable/ - Mypy na PyPi Optional static typing for Python
https://pypi.org/project/mypy/ - 5 Reasons Why You Should Use Type Hints In Python
https://www.youtube.com/watch?v=dgBCEB2jVU0 - Python Typing – Type Hints & Annotations
https://www.youtube.com/watch?v=QORvB-_mbZ0 - What Problems Can TypeScript Solve?
https://www.typescriptlang.org/why-create-typescript - Type system (Wikipedia)
https://en.wikipedia.org/wiki/Type_system - PYPL PopularitY of Programming Language
https://pypl.github.io/PYPL.html - How Different Programming Languages Handle Data Types
https://learncodingusa.com/programming-languages-data-types/ - Exploring Data Types in Different Coding Languages
https://learncodingusa.com/data-types-in-coding-languages/
