Luau: rozšíření jazyka Lua o statické typové kontroly a typovou inferenci

Dnes
Doba čtení: 36 minut

Sdílet

Autor: Depositphotos
Jazyk Lua je postaven dynamickém typovém systému, v němž jsou typy vztaženy k hodnotám a nikoli k proměnným či parametrům funkcí. Pro rozsáhlejší projekty je však výhodnější využívat statické typové kontroly.

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
  1. Statické a dynamické typové systémy
  2. Statické typové kontroly
  3. Dynamické typové kontroly
  4. Postupný přechod od jazyků s dynamickým typovým systémem k jazykům s volitelnými typy
  5. Statické typové kontroly implementované v projektu Luau
  6. Praktická část: překlad projektu Luau
  7. Malá odbočka: Lua vs Luau z pohledu spotřeby zdrojů (resources)
  8. Základní datové typy použité v Luau
  9. Přečtení informace o typu v čase běhu programu (runtime)
  10. Přidání typových anotací do zdrojového kódu, statická typová kontrola
  11. Typ any
  12. Typová inference a řízení „přísnosti“ statické typové kontroly
  13. Základní typové anotace funkcí
  14. Typové anotace tabulek
  15. Tabulka s prvky indexovanými přirozenými čísly
  16. Tabulka použitá ve funkci slovníku
  17. Články a seriály o jazyku Lua a souvisejících technologiích na Rootu
  18. Repositář s demonstračními příklady
  19. 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í.

Logo projektu Lua

 Obrázek 1: Logo programovacího jazyka Lua (neanimovaná varianta).

Autor: PUC Rio, podle licence: Rights Managed

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.

Logo projektu Luau

 Obrázek 2: Logo programovacího jazyka Luau.

Autor: Roblox, podle licence: Rights Managed

Poznámka: Luau je ve skutečnosti v porovnání s původním interpretrem jazyka Lua či JIT překladačem LuaJIT mnohem rozsáhlejší projekt. Jazyk je rozšířen o další datové typy, řídicí struktury atd. Do značné míry tak ovšem ztrácíme minimalismus a eleganci, které Lua přinesla (a zrovna tyto dvě vlastnosti mnohdy v IT projektech chybí).

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 ?
Poznámka: poměrně často se můžeme setkat s kombinací statických a dynamických typových kontrol. Příkladem může být programovací jazyk Java, který je (alespoň většinou) staticky typovaný, ovšem některé operace (typicky spojené s polymorfismem) vyžadují, aby se kontrola typů prováděla až v době běhu aplikace. Totéž do jisté míry platí i pro jazyk Go.

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
Poznámka: to ovšem znamená, že přidání podpory pro statický typový systém vyžaduje zásah do syntaxe i sémantiky programovacího jazyka. Záleží na původním návrhu jazyka, do jaké míry je takové rozšíření přirozené. V případě jazyka Lua (a rozšíření zavedeného v Luau) jsou některé úpravy velmi elegantní, jiné potom spíše diskutabilní (zápis typů u tabulek atd.).

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
Poznámka: Lua nainstalovaná v operačním systému je ovšem většinou přeložena odlišným způsobem. Binární soubory pouze „obalují“ knihovnu liblua-5.verze.so:
$ 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
Poznámka: buffer je možné využít následujícím způsobem: nejprve alokujeme buffer o určité velikosti. Potom je do něj možné serializovat hodnoty různého typu a zapisovat je na určité offsety (v rámci bufferu). Totéž platí pro čtení (deserializaci). Obsah bufferu je ovšem taktéž možné převést na řetězec, pokud to dává smysl:
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))
Poznámka: povšimněte si způsobu zápisu tetu, zda existují symboly newproxy a buffer ještě předtím, než se budou volat pro konstrukci hodnoty daného typu.

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
Poznámka: podobně je tomu například i v Pythonu, ve kterém Mypy taktéž neodhalí stejnou chybu. Stručná ukázka:
$ 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 foobar, 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
Poznámka: popravdě řečeno mi nepřipadne tento režim v praxi ničím užitečný; snad jen při refaktoringu rozsáhlých zdrojových kódů s postupným přidáváním typových anotací.

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
Poznámka: opět tedy není zjištěn problém na posledním řádku, na kterém funkci předáváme větší množství parametrů.

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
Poznámka: otázkou je, zda je vlastně korektní zavolat funkci swap s jediným parametrem, což je častá chyba. V jazyce Lua to ve skutečnosti možné je – hodnota nepředaného parametru bude nastavena na nil. Na tento problém nás mohou upozornit právě statické typové kontroly.

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:

  1. 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.
  2. 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).
  3. 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
Poznámka: příště se zmíníme o typu optional, který tyto situace dokáže popsat.

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:

Školení Zabbix

$ ./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* }'
Poznámka: ve skutečnosti je typový systém, který je v projektu Luau implementován, rozsáhlejší a do jisté míry i dosti sofistikovaný. S podrobnostmi se seznámíme v navazujícím článku, ve kterém celé téma dokončíme.

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

  1. Seriál Programovací jazyk Lua
    https://www.root.cz/seria­ly/programovaci-jazyk-lua/
  2. Seriál Torch: framework pro strojové učení
    https://www.root.cz/serialy/torch-framework-pro-strojove-uceni/
  3. Skriptovací jazyk Lua v aplikacích naprogramovaných v Go
    https://www.root.cz/clanky/skriptovaci-jazyk-lua-v-aplikacich-naprogramovanych-v-go/
  4. LuaJIT – Just in Time překladač pro programovací jazyk Lua
    https://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua/
  5. 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/
  6. Proudy (streams) podporované systémem Redis
    https://www.root.cz/clanky/proudy-streams-podporovane-systemem-redis/
  7. Proudy (streams) podporované systémem Redis (dokončení)
    https://www.root.cz/clanky/proudy-streams-podporovane-systemem-redis-dokonceni/
  8. Databáze Redis (nejenom) pro vývojáře používající Python
    https://www.root.cz/clanky/databaze-redis-nejenom-pro-vyvojare-pouzivajici-python/
  9. 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/
  10. 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/
  11. 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/
  12. Lua: univerzální distribuce programovacího jazyka Lua
    https://www.root.cz/clanky/ulua-univerzalni-distribuce-programovaciho-jazyka-lua/
  13. 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_an­notations_1.lua
4 type_annotations_2.lua pokus o provedení operace pro nekompatibilní datové typy https://github.com/tisnik/luau-examples/blob/master/type_an­notations_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_an­notations_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_nochec­k.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_non­strict.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

  1. Repositář projektu luau
    https://github.com/luau-lang/luau
  2. Oficiální vydání
    https://github.com/luau-lang/luau
  3. An introduction to Luau
    https://luau.org/getting-started/
  4. Luau Programming Full Course (2026) – For Beginners
    https://www.youtube.com/watch?v=9kdRHP-FZCA
  5. Roblox: Luau
    https://create.roblox.com/docs/luau
  6. How to start learning luau?
    https://devforum.roblox.com/t/how-to-start-learning-luau/3823179
  7. Luau (programming language)
    https://en.wikipedia.org/wi­ki/Luau_(programming_langu­age)
  8. Gradual typing
    https://en.wikipedia.org/wi­ki/Gradual_typing
  9. Gradual typing in Python
    https://www.geeksforgeeks­.org/python/gradual-typing-in-python/
  10. Łukasz Langa: Gradual Typing of Production Applications
    https://www.youtube.com/wat­ch?v=zKre4DKAB30
  11. lupa na GitHubu
    https://github.com/scoder/lupa
  12. lupa na PyPi
    https://pypi.org/project/lupa/
  13. Lunatic Python
    https://labix.org/lunatic-python
  14. Seriál Programovací jazyk Lua
    https://www.root.cz/seria­ly/programovaci-jazyk-lua/
  15. Python na Rootu
    https://www.root.cz/n/python/
  16. Lua na Wikipedii
    https://en.wikipedia.org/wiki/Lua
  17. Python (programming language) na Wikipedii
    https://en.wikipedia.org/wi­ki/Python_(programming_lan­guage)
  18. Languages that compile to Lua
    https://github.com/hengestone/lua-languages?tab=readme-ov-file#languages-that-compile-to-lua
  19. Repositář projektu Lua Fun
    https://github.com/luafun/luafun
  20. The Lua VM, on the Web
    https://kripken.github.io/lu­a.vm.js/lua.vm.js.html
  21. Lua.vm.js REPL
    https://kripken.github.io/lu­a.vm.js/repl.html
  22. lua2js
    https://www.npmjs.com/package/lua2js
  23. lua2js na GitHubu
    https://github.com/basicer/lua2js-dist
  24. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  25. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  26. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
  27. ULua: Universal Lua Distribution
    https://ulua.io/index.html
  28. deque
    https://github.com/catwell/cw-lua/blob/master/deque/deque.test.lua
  29. Repositář projektu Lua Fun
    https://github.com/luafun/luafun
  30. Lua Functional 0.1.3 documentation
    https://luafun.github.io/re­ference.html
  31. Lua Functional: Getting Started
    https://luafun.github.io/get­ting_started.html
  32. LuaRocks
    https://luarocks.org/
  33. Documentation
    Dokumentace ke správci balíčků LuaRocks
  34. Rockspec knihovny Fun
    https://raw.githubusercon­tent.com/luafun/luafun/mas­ter/fun-scm-1.rockspec
  35. Awesome Lua – A curated list of quality Lua packages and resources.
    https://github.com/LewisJEllis/awesome-lua
  36. LuaJIT
    https://luajit.org/
  37. Running LuaJIT
    https://luajit.org/running.html
  38. LuaJIT na GitHubu
    https://github.com/luajit
  39. Repositář projektu Moses
    https://github.com/Yonaba/Moses/
  40. Návod k použití knihovny Moses
    https://github.com/Yonaba/Mo­ses/blob/master/doc/tutori­al.md
  41. Lambda the Ultimate: Coroutines in Lua
    http://lambda-the-ultimate.org/node/438
  42. Coroutines Tutorial
    http://lua-users.org/wiki/CoroutinesTutorial
  43. Lua Coroutines Versus Python Generators
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
  44. Programming in Lua 9.1 – Coroutine Basics
    http://www.lua.org/pil/9.1.html
  45. How to understand clojure's lazy-seq
    https://stackoverflow.com/qu­estions/44095400/how-to-understand-clojures-lazy-seq
  46. Lua Implementations
    http://lua-users.org/wiki/LuaImplementations
  47. Generator (computer programming)
    https://en.wikipedia.org/wi­ki/Generator_(computer_pro­gramming)
  48. Lua 5.2 Reference Manual: 6.10 – The Debug Library
    http://www.lua.org/manual/5­.2/manual.html#6.10
  49. Issues k projektu LuaFun
    https://github.com/luafun/lu­afun/issues
  50. Archived | Embed Lua for scriptable apps
    https://developer.ibm.com/tutorials/l-embed-lua/
  51. Embedding Lua
    https://www.oreilly.com/li­brary/view/lua-quick-start/9781789343229/3a6f3daf-f74c-4a25-a125–584da58568e4.xhtml
  52. 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/
  53. Which Programming Languages Index by One?
    https://therenegadecoder.com/co­de/which-programming-languages-index-by-one/
  54. 2Zero-based indexing considered harmful
    https://craftofcoding.wor­dpress.com/2021/03/26/zero-based-indexing-considered-harmful/
  55. Lua downloads
    https://www.lua.org/download.html
  56. An introduction to Luau types
    https://luau.org/types/
  57. PEP 585 – Type Hinting Generics In Standard Collections
    https://peps.python.org/pep-0585/
  58. mypy homepage
    https://www.mypy-lang.org/
  59. mypy documentation
    https://mypy.readthedocs.i­o/en/stable/
  60. Mypy na PyPi Optional static typing for Python
    https://pypi.org/project/mypy/
  61. 5 Reasons Why You Should Use Type Hints In Python
    https://www.youtube.com/wat­ch?v=dgBCEB2jVU0
  62. Python Typing – Type Hints & Annotations
    https://www.youtube.com/watch?v=QORvB-_mbZ0
  63. What Problems Can TypeScript Solve?
    https://www.typescriptlang.org/why-create-typescript
  64. Type system (Wikipedia)
    https://en.wikipedia.org/wi­ki/Type_system
  65. PYPL PopularitY of Programming Language
    https://pypl.github.io/PYPL.html
  66. How Different Programming Languages Handle Data Types
    https://learncodingusa.com/programming-languages-data-types/
  67. Exploring Data Types in Different Coding Languages
    https://learncodingusa.com/data-types-in-coding-languages/

Autor článku

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