Obsah
1. Generické datové typy v jazyce Go?
2. Beztypové a jednoúčelové kontejnery
5. Použití generických datových typů v Javě
7. Generické datové typy v programovacím jazyku Rust
9. Generické funkce a silná typová kontrola v Rustu
10. Stav podpory generických datových typů v jazyce Go
11. Příklad omezení současné verze Go
13. Vygenerování kódu pro funkci pro součet dvou čísel projektem Genny
14. Změna názvu funkce s generickým numerickým datovým typem
15. Vygenerování testů generické funkce pro součet
16. Anonymní funkce v expandovaném kódu
17. Negenerický binární strom (s konkrétními typy hodnot uzlů)
19. Repositář s demonstračními příklady
1. Generické datové typy v jazyce Go?
Již v úvodní části seriálu o programovacím jazyku Go jsme si mj. řekli, že tento jazyk byl navržen takovým způsobem, že obsahuje pouze ty syntaktické a sémantické prvky, na nichž se shodli všichni tři autoři tohoto projektu. Všechny vlastnosti, u nichž nebylo na sto procent jisté, že jsou správně navrženy a že jejich implementace nebude zpomalovat překladač ani výsledný zkompilovaný a slinkovaný kód, nebyly do první verze programovacího jazyka Go přidány. Týká se to některých vlastností, které mohou programátorům chybět – například výjimek, tříd (a na třídách postavené větvi objektově orientovaného programování) a taktéž generických datových typů a generických funkcí. A zejména neexistence generických datových typů může být pro některé typy aplikací značně omezující, protože je například složité implementovat obecné a současně i typově bezpečné datové struktury typu graf, strom, zásobník, fronta atd.
V dnešním článku se nejprve ve stručnosti seznámíme s tím, jakým způsobem jsou generické datové typy implementovány v Javě a taktéž v Rustu, tedy v těch programovacích jazycích, jejichž niky se částečně překrývají s oblastí použití jazyka Go. Ve druhé části článku si ukážeme způsob použití projektu nazvaného Genny, který – alespoň částečně – dokáže do Go zavést alespoň minimální podporu generických datových typů. Využívá se přitom generování zdrojového kódu v Go, což je zcela odlišný postup, než který známe právě z Javy nebo z Rustu (kde je „duplikátní“ kód vytvářen buď v době překladu, nebo je celá problematika vyřešena pomocí takzvané type erasure prováděné taktéž v době překladu).
2. Beztypové a jednoúčelové kontejnery
V některých programovacích jazycích (a nejedná se pouze o dynamicky typované jazyky) se setkáme s takzvanými beztypovými, popř. s jednoúčelovými kontejnery, kterými můžeme do jisté míry nahradit kontejnery s generickými typy. Typicky se jedná o implementace seznamů, front, zásobníků, různých typů stromů, obecných orientovaných i neorientovaných grafů atd. Beztypové kontejnery jsou většinou založeny na vlastnostech třídního OOP (dědění a polymorfismus) a většinou taktéž na tom, že hierarchie tříd mívá v mnoha jazycích společného předka. To tedy znamená, že pokud vytvoříme kontejner (řekněme seznam) pro prvky typu „instance třídy na vrcholu hierarchie tříd“, bude možné do takové třídy uložit jakýkoli objekt, ovšem za tu cenu, že se ztrácí informace o uloženém typu (tu je nutné získávat pro každý prvek zvlášť, pokud to jazyk díky RTTI umožňuje).
Naopak je mnohdy možné vytvořit takzvané jednoúčelové kontejnery. Ty dokážou ukládat prvky jediného typu, popř. v OOP jazycích i odvozeného typu (potomci třídy). Popř. se může jednat o prvky implementující či splňující nějaké rozhraní. Typickým příkladem může být pole či řez v jazyku Go. Při použití jednoúčelových kontejnerů se informace o typu prvků neztratí a naopak je striktně kontrolována překladačem. Nevýhoda je ovšem zřejmá – pro každý datový typ je mnohdy nutné vytvořit prakticky stejný kontejner, jehož implementace se mnohdy odlišuje pouze v několika maličkostech.
3. Statická genericita
Generické datové typy a generické funkce lze realizovat rozličnými způsoby. Pokud samotný programovací jazyk genericitu nepodporuje, ovšem má rozumný makrosystém, lze použít (či spíše zneužít) právě tento makrosystém, kdy makra budou expandována na konkrétní datový typ (například komplexní číslo s položkami typu float), popř. na konkrétní funkci (s tím, že jméno funkce bude muset být nějakým způsobem unikátní). Jedná se ovšem o velmi křehké řešení – mnoho chybových hlášení bude používat expandovaný kód, který uživatel nenapsal atd.
V některých programovacích jazycích, například v Javě, se používá odlišný způsob, při němž se ve zdrojovém kódu konkrétní datový typ (vhodným způsobem) zapíše a překladač ho tedy zpracuje a použije pro případné typové kontroly. Ovšem interně – v generovaném kódu nebo bajtkódu – se použije nějaký obecný společný nadtyp, typicky třída Object (v závislosti na konkrétní hierarchii tříd a datových typů). Prakticky stejným způsobem je vyřešeno vytváření funkcí a metod z generických funkcí a metod.
4. Dynamická genericita
V případě, že překladač programovacího jazyka vytváří kód běžící ve virtuálním stroji, je možné generické datové typy, popř. i generické funkce a metody vytvářet právě virtuálním strojem, a to přímo za běhu aplikace. Toto řešení má některé výhody (vytvoří se pouze tolik variant, kolik je skutečně zapotřebí), ovšem samozřejmě za tuto možnost zaplatíme pomalejším během a mnohdy i většími paměťovými nároky.
5. Použití generických datových typů v Javě
Nejprve se alespoň ve stručnosti podívejme na způsob použití generických datových typů v programovacím jazyku Java. Uvedeme si jeden z nejtypičtějších motivačních příkladů, na němž se například v učebnicích ukazují výhody generických datových typů v silně typovaných programovacích jazycích. V příkladu je vytvořen obecný seznam (jehož konkrétní implementace je založena na sekvenci prvků uložených v poli). Do tohoto seznamu můžeme vkládat libovolné objekty, přesněji řečeno instance jakékoli třídy. Proč tomu tak je? Seznam je kontejnerem pro objekty typu Object a právě třída Object leží na vrcholu hierarchie všech tříd Javy (jedná se o stromovou strukturu s jediným kořenem). Platí zde tedy jeden z principů třídního OOP – potomek může nahradit předka. Ve zdrojovém kódu vidíme, že do seznamu lze vložit i celé číslo, ovšem v tomto případě je interně použit takzvaný boxing, v němž je numerická hodnota nahrazena objektem, zde konkrétně instancí třídy Integer:
import java.util.List; import java.util.ArrayList; import java.awt.Color; public class Test1 { public static void main(String[] args) { List l = new ArrayList(); l.add(new Object()); l.add("foobar"); l.add(42); l.add(Color.green); for (Object i : l) { System.out.println(i); } } }
Mimochodem, interně, tedy na úrovni bajtkódu, vypadá boxing následovně (volá se statická metoda Integer.valueOf):
32: bipush 42 34: invokestatic #7 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 37: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
Výše uvedené řešení má mnoho nevýhod. Překladač (nikoli ovšem runtime) ztrácí informace o tom, jaké typy objektů jsou vlastně v seznamu uloženy. Proto například není možné přeložit následující program, a to přesto, že sémanticky je zdánlivě v pořádku – do seznamu jsme uložili pouze řetězce, takže by mělo být možné volat pro všechny prvky seznamu metodu length(). Ovšem z pohledu překladače jsou všechny prvky (netypového) seznamu typu Object, takže to přímo možné není:
import java.util.List; import java.util.ArrayList; import java.awt.Color; public class Test2 { public static void main(String[] args) { List l = new ArrayList(); l.add("foo"); l.add("bar"); l.add("baz"); String s = l.get(0); System.out.println(s.length()); } }
Výsledek pokusu o překlad dopadne neslavně:
Test2.java:8: warning: [unchecked] unchecked call to add(E) as a member of the raw type List l.add("foo"); ^ where E is a type-variable: E extends Object declared in interface List Test2.java:9: warning: [unchecked] unchecked call to add(E) as a member of the raw type List l.add("bar"); ^ where E is a type-variable: E extends Object declared in interface List Test2.java:10: warning: [unchecked] unchecked call to add(E) as a member of the raw type List l.add("baz"); ^ where E is a type-variable: E extends Object declared in interface List Test2.java:12: error: incompatible types String s = l.get(0); ^ required: String found: Object 1 error 3 warnings
Jedno z řešení, které pochází z prehistorické doby Javy 1.4, je založeno na explicitním přetypování prvku:
import java.util.List; import java.util.ArrayList; import java.awt.Color; public class Test3 { public static void main(String[] args) { List l = new ArrayList(); l.add("foo"); l.add("bar"); l.add("baz"); String s = (String)l.get(0); System.out.println(s.length()); } }
Na úrovni bajtkódu vypadá načtení prvku, kontrola jeho typu a vytištění délky řetězce následovně:
// získání prvku z kolekce, kontrola typu objektu 35: aload_1 36: iconst_0 37: invokeinterface #8, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 42: checkcast #9 // class java/lang/String 45: astore_2 // metoda, která se bude volat 46: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream; // získání délky řetězce s jeho výpisem 49: aload_2 50: invokevirtual #11 // Method java/lang/String.length:()I 53: invokevirtual #12 // Method java/io/PrintStream.println:(I)V
Mnohem lepší řešení spočívá ve specifikaci typu prvků kolekce. Jedná se o informaci použitou překladačem, která mj. umožňuje provádět lepší typovou kontrolu:
import java.util.List; import java.util.ArrayList; import java.awt.Color; public class Test4 { public static void main(String[] args) { List<String> l = new ArrayList<String>(); l.add(new Object()); l.add("foobar"); l.add(42); l.add(Color.green); for (String s : l) { System.out.println(s.length()); } } }
Tento demonstrační příklad se ovšem opět nepřeloží, a to z toho důvodu, že se do seznamu, který má obsahovat pouze řetězce, snažíme přidat prvky odlišných typů. Chybové hlášení vypadá na starší verzi Javy následovně:
Test4.java:8: error: no suitable method found for add(Object) l.add(new Object()); ^ method List.add(int,String) is not applicable (actual and formal argument lists differ in length) method List.add(String) is not applicable (actual argument Object cannot be converted to String by method invocation conversion) method Collection.add(String) is not applicable (actual argument Object cannot be converted to String by method invocation conversion) Test4.java:10: error: no suitable method found for add(int) l.add(42); ^ method List.add(int,String) is not applicable (actual and formal argument lists differ in length) method List.add(String) is not applicable (actual argument int cannot be converted to String by method invocation conversion) method Collection.add(String) is not applicable (actual argument int cannot be converted to String by method invocation conversion) Test4.java:11: error: no suitable method found for add(Color) l.add(Color.green); ^ method List.add(int,String) is not applicable (actual and formal argument lists differ in length) method List.add(String) is not applicable (actual argument Color cannot be converted to String by method invocation conversion) method Collection.add(String) is not applicable (actual argument Color cannot be converted to String by method invocation conversion) 3 errors
V korektně zapsaném zdrojovém kódu jsou do seznamu ukládány pouze řetězce:
import java.util.List; import java.util.ArrayList; import java.awt.Color; public class Test5 { public static void main(String[] args) { List<String> l = new ArrayList<String>(); l.add("foo"); l.add("bar"); l.add("baz"); l.add(Integer.toString(42)); for (String s : l) { System.out.println(s.length()); } } }
Díky tomu, že překladač zná typ kolekce (a tedy i typ prvků ukládaných do seznamu), je možné programovou smyčku, která vytiskne délky všech řetězců v seznamu, přeložit do bajtkódu následujícím způsobem:
47: aload_1 48: invokeinterface #9, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 53: astore_2 // začátek programové smyčky 54: aload_2 // test, zda kolekce obsahuje další prvek 55: invokeinterface #10, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z // pokud prvek neexistuje, ukončení programové smyčky 60: ifeq 86 63: aload_2 // získání prvku z kolekce, kontrola typu objektu 64: invokeinterface #11, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 69: checkcast #12 // class java/lang/String 72: astore_3 // metoda, která se bude volat 73: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream; // získání délky řetězce s jeho výpisem 76: aload_3 77: invokevirtual #14 // Method java/lang/String.length:()I 80: invokevirtual #15 // Method java/io/PrintStream.println:(I)V // pokračování smyčky (začátek další iterace s testem na začátku) 83: goto 54
6. Type erasure v Javě
V Javě se navíc setkáme s takzvaným type erasure. Jedná se o odstranění informace o generickém typu překladačem při vytváření bajtkódu. To má několik důsledků – striktní typová kontrola je prováděna v době překladu (compile time), ovšem typová informace (například o typu prvků kontejneru) je v čase běhu (runtime) ztracena. Ověřit si to ostatně můžeme na následujícím jednoduchém příkladu, v němž jsou vytvořeny dvě kolekce, každá s prvky jiného typu. Následně si necháme vypsat jména kolekcí a dokonce můžeme porovnat, zda jsou kolekce implementovány stejnou třídou či nikoli:
import java.util.Collection; import java.util.ArrayList; import java.awt.Color; public class Test6 { public static void main(String[] args) { Collection<String> l1 = new ArrayList<String>(); Collection<Integer> l2 = new ArrayList<Integer>(); System.out.println(l1.getClass().getName()); System.out.println(l2.getClass().getName()); System.out.println(l1.getClass() == l2.getClass()); } }
Po spuštění tohoto demonstračního příkladu získáme dvakrát stejné jméno třídy. Na posledním řádku je patrné, že jsou třídy (z pohledu virtuálního stroje v Runtime) skutečně shodné:
java.util.ArrayList java.util.ArrayList true
Jen pro úplnost si ukažme, jak je takové porovnání tříd dvou objektů (tedy využití RTTI) realizováno v bajtkódu:
// zavolání getClass u prvního objektu 45: aload_1 46: invokevirtual #5 // Method java/lang/Object.getClass:()Ljava/lang/Class; // zavolání getClass u prvního objektu 49: aload_2 50: invokevirtual #5 // Method java/lang/Object.getClass:()Ljava/lang/Class; // porovnání se skokem 53: if_acmpne 60 56: iconst_1 // skok za "else" 57: goto 61 60: iconst_0
7. Generické datové typy v programovacím jazyku Rust
Generické datové typy jsou podporovány i dalším (i když mnohdy nepřímým) konkurentem Go – programovacím jazykem Rust. Generické typy v Rustu mohou při správném použití zjednodušit tvorbu znovupoužitelného programového kódu a současně zajistit silnou typovou kontrolu při překladu (což jsou bez použití generických typů mnohdy současně nesplnitelné požadavky). Podívejme se nejprve na jednoduchý motivační příklad, v němž prozatím nejsou použity generické datové typy. Jedná se o implementaci datového typu (struktury) představujícího komplexní čísla. První verze vypadá takto – Complex je zde skutečně pouhá datová struktura:
struct Complex { real: f32, imag: f32, } fn main() { let c1 = Complex{real:10.0, imag:20.0}; let c2 = Complex{real:10.1, imag:20.1}; let c3 = Complex{real:10.2, imag:20.2}; let c4 = Complex{real:1., imag:2.}; println!("{}+{}i", c1.real, c1.imag); println!("{}+{}i", c2.real, c2.imag); println!("{}+{}i", c3.real, c3.imag); println!("{}+{}i", c4.real, c4.imag); }
V tomto zdrojovém kódu je deklarováno, že reálná a imaginární složka je představována datovým typem float/single, což je překladačem striktně hlídáno (a to mnohem silněji, než například v céčku, podobně silně, jako je tomu v Go). Pokud se například pokusíme do reálné či imaginární složky vložit celé číslo, dojde k chybě při překladu:
struct Complex { real: f32, imag: f32, } fn main() { let c1 = Complex{real:10, imag:20}; let c2 = Complex{real:10.1, imag:20.1}; let c3 = Complex{real:10.2, imag:20.2}; let c4 = Complex{real:1, imag:2}; println!("{}+{}i", c1.real, c1.imag); println!("{}+{}i", c2.real, c2.imag); println!("{}+{}i", c3.real, c3.imag); println!("{}+{}i", c4.real, c4.imag); }
Při pokusu o překlad tohoto příkladu by se mělo vypsat následující chybové hlášení, a to pro všechny výskyty hodnoty odlišného datového typu:
error[E0308]: mismatched types --> test.rs:7:27 | 7 | let c1 = Complex{real:10, imag:20}; | ^^ expected f32, found integral variable | = note: expected type `f32` = note: found type `{integer}` error[E0308]: mismatched types --> test.rs:7:36 | 7 | let c1 = Complex{real:10, imag:20}; | ^^ expected f32, found integral variable | = note: expected type `f32` = note: found type `{integer}` error[E0308]: mismatched types --> test.rs:10:27 | 10 | let c4 = Complex{real:1, imag:2}; | ^ expected f32, found integral variable | = note: expected type `f32` = note: found type `{integer}` error[E0308]: mismatched types --> test.rs:10:35 | 10 | let c4 = Complex{real:1, imag:2}; | ^ expected f32, found integral variable | = note: expected type `f32` = note: found type `{integer}` error: aborting due to 4 previous errors
Předchozí demonstrační příklad sice fungoval korektně a podle všech předpokladů, ovšem jen ve chvíli, kdy nám postačovalo použití komplexních čísel, jejichž složky byly reprezentovány typem float/single. Ovšem je jen otázkou času, kdy nějaký vývojář bude chtít použít podobný kód, ovšem například pro datový typ double, pro typ „zlomek“ atd. V takovém případě může být nejvýhodnější deklaraci datové struktury upravit takovým způsobem, aby se konkrétní typ složek komplexního čísla rozpoznal až v době překladu na základě typů konkrétních hodnot či výrazů použitých pro konstrukci datové struktury.
Programovací jazyk Rust tento přístup podporuje, protože umožňuje následující styl deklarace (znak T není klíčovým slovem, ovšem je v kontextu generických datových typů často používán, takže tento úzus taktéž dodržíme):
struct Complex<T> { real: T, imag: T, }
Tento zápis znamená, že se za T při překladu doplní konkrétní rozpoznaný datový typ, což si ostatně můžeme snadno vyzkoušet:
fn main() { let c1 = Complex{real:10, imag:20}; let c2 = Complex{real:10.1, imag:20.1}; let c3 = Complex{real:10.2f64, imag:20.2f64}; let c4 = Complex{real:true, imag:false}; println!("{}+{}i", c1.real, c1.imag); println!("{}+{}i", c2.real, c2.imag); println!("{}+{}i", c3.real, c3.imag); println!("{}+{}i", c4.real, c4.imag); }
Ve chvíli, kdy se pokusíme o kombinaci různých typů, budeme na to upozorněni překladačem, protože typ reálné i imaginární složky musí být totožný:
fn main() { let c1 = Complex{real:10, imag:true}; println!("{}+{}i", c1.real, c1.imag); }
S výsledkem:
error[E0308]: mismatched types --> test.rs:7:36 | 7 | let c1 = Complex{real:10, imag:true}; | ^^^^ expected integral variable, found bool | = note: expected type `{integer}` = note: found type `bool` error: aborting due to previous error
Jinými slovy – typ datové struktury Complex je parametrizovatelný, ovšem současně je stále zajištěna typová kontrola (nejedná se tedy o nic ve smyslu Complex(Object, Object), tedy o řešení, které jsme viděli ve světě Javy 1.4 :-).
8. Generické funkce v Rustu
V programovacím jazyku Rust je možné kromě deklarace generických datových typů vytvářet i generické funkce, tj. funkce, u nichž lze specifikovat parametrizovatelné typy argumentů i návratový typ. Podívejme se nyní na sice poněkud umělý, ale o to kratší demonstrační příklad. V tomto příkladu nejprve deklarujeme výčtový typ a následně funkci, která akceptuje dva parametry typu i32 (celé číslo se znaménkem) a třetí parametr, na základě jehož hodnoty funkce vrátí buď první či druhý parametr. Nejprve si povšimněte, jak se používá výčtový typ (má vlastní jmenný prostor, proto se zapisuje stylem Item::First a nikoli pouze First). Použití konstrukce match je v tomto případě idiomatické a mnohem lepší, než pokus o použití if, a to z toho důvodu, že překladač sám zkontroluje, zda v konstrukci match korektně reagujeme na všechny možné vstupy (což samozřejmě děláme :-):
enum Item { First, Second, } fn select_item(first_item:i32, second_item:i32, item:Item) -> i32 { match item { Item::First => first_item, Item::Second => second_item, } } fn main() { let x = 10; let y = 20; println!("1st item = {}", select_item(x, y, Item::First)); println!("2nd item = {}", select_item(x, y, Item::Second)); }
Po překladu a spuštění by se na standardní výstup měly vypsat následující dva řádky znamenající, že poprvé funkce select_item vybrala a vrátila první argument (resp. zde jeho kopii!) a podruhé druhý argument:
1st item = 10 2nd item = 20
Funkce select_item v podobě, v jaké jsme si ji ukázali, není příliš použitelná ani obecná, protože ji ve skutečnosti lze volat pouze s parametry typu i32. Pokusme se tedy vytvořit podobnou funkci, ovšem generickou. V tomto případě to znamená, že typy prvních dvou parametrů musí být shodné a musí odpovídat návratovému typu funkce – ta totiž nemá provádět žádné konverze, pouze vybírat mezi prvním a druhým argumentem. Takto navržená generická funkce může vypadat následovně (povšimněte si především zápisu <T> za jménem funkce):
fn select_item<T>(first_item:T, second_item:T, item:Item) -> T { match item { Item::First => first_item, Item::Second => second_item, } }
Nově deklarovanou funkci je následně možné použít pro různé typy argumentů, samozřejmě za předpokladu, že oba dva argumenty budou stejného typu. Opět si to ukažme:
enum Item { First, Second, } fn select_item<T>(first_item:T, second_item:T, item:Item) -> T { match item { Item::First => first_item, Item::Second => second_item, } } fn main() { let x = 10.1; let y = 20.2; println!("1st item = {}", select_item(x, y, Item::First)); println!("2nd item = {}", select_item(x, y, Item::Second)); let z:i32 = 10; let w:i32 = 20; println!("1st item = {}", select_item(z, w, Item::First)); println!("2nd item = {}", select_item(z, w, Item::Second)); let a = true; let b = false; println!("1st item = {}", select_item(a, b, Item::First)); println!("2nd item = {}", select_item(a, b, Item::Second)); }
Po spuštění tohoto demonstračního příkladu získáme následující řádky vypsané na standardní výstup:
1st item = 10.1 2nd item = 20.2 1st item = 10 2nd item = 20 1st item = true 2nd item = false
9. Generické funkce a silná typová kontrola v Rustu
V předchozí kapitole jsme si řekli, že překladač pro novou podobu funkce select_item skutečně kontroluje, zda jsou typy prvních dvou argumentů shodné. Pojďme si toto tvrzení ověřit:
enum Item { First, Second, } fn select_item<T>(first_item:T, second_item:T, item:Item) -> T { match item { Item::First => first_item, Item::Second => second_item, } } fn main() { let x = 10.1; let y = 20; println!("1st item = {}", select_item(x, y, Item::First)); println!("2nd item = {}", select_item(x, y, Item::Second)); let z:f32 = 10; let w:i32 = 20; println!("1st item = {}", select_item(z, w, Item::First)); println!("2nd item = {}", select_item(z, w, Item::Second)); let a = 10; let b = false; println!("1st item = {}", select_item(a, b, Item::First)); println!("2nd item = {}", select_item(a, b, Item::Second)); }
Spusťme nyní překladač na tento zdrojový kód, aby bylo patrné, jak pracuje statická typová kontrola (navíc je ukázána i kontrola typů proměnných a hodnot přiřazovaných do proměnných, což je ostatně jedna ze základních činností překladače Rustu):
error[E0308]: mismatched types --> test.rs:16:46 | 16 | println!("1st item = {}", select_item(x, y, Item::First)); | ^ expected floating-point variable, found integral variable <std macros>:2:27: 2:58 note: in this expansion of format_args! <std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>) test.rs:16:5: 16:63 note: in this expansion of println! (defined in <std macros>) | = note: expected type `{float}` = note: found type `{integer}` error[E0308]: mismatched types --> test.rs:17:46 | 17 | println!("2nd item = {}", select_item(x, y, Item::Second)); | ^ expected floating-point variable, found integral variable <std macros>:2:27: 2:58 note: in this expansion of format_args! <std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>) test.rs:17:5: 17:64 note: in this expansion of println! (defined in <std macros>) | = note: expected type `{float}` = note: found type `{integer}` error[E0308]: mismatched types --> test.rs:19:17 | 19 | let z:f32 = 10; | ^^ expected f32, found integral variable | = note: expected type `f32` = note: found type `{integer}` error[E0308]: mismatched types --> test.rs:21:46 | 21 | println!("1st item = {}", select_item(z, w, Item::First)); | ^ expected f32, found i32 <std macros>:2:27: 2:58 note: in this expansion of format_args! <std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>) test.rs:21:5: 21:63 note: in this expansion of println! (defined in <std macros>) error[E0308]: mismatched types --> test.rs:22:46 | 22 | println!("2nd item = {}", select_item(z, w, Item::Second)); | ^ expected f32, found i32 <std macros>:2:27: 2:58 note: in this expansion of format_args! <std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>) test.rs:22:5: 22:64 note: in this expansion of println! (defined in <std macros>) error[E0308]: mismatched types --> test.rs:26:46 | 26 | println!("1st item = {}", select_item(a, b, Item::First)); | ^ expected integral variable, found bool <std macros>:2:27: 2:58 note: in this expansion of format_args! <std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>) test.rs:26:5: 26:63 note: in this expansion of println! (defined in <std macros>) | = note: expected type `{integer}` = note: found type `bool` error[E0308]: mismatched types --> test.rs:27:46 | 27 | println!("2nd item = {}", select_item(a, b, Item::Second)); | ^ expected integral variable, found bool <std macros>:2:27: 2:58 note: in this expansion of format_args! <std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>) test.rs:27:5: 27:64 note: in this expansion of println! (defined in <std macros>) | = note: expected type `{integer}` = note: found type `bool` error: aborting due to 7 previous errors
10. Stav podpory generických datových typů v jazyce Go
V současnosti nejsou generické datové typy ani generické funkce v programovacím jazyce Go přímo podporovány, i když existuje hned několik návrhů na jejich zavedení do nové verze jazyka, která nese prozatímní označení Go 2. Zkusme se podívat, jaké problémy neexistence generických typů může přinášet. Začněme zcela jednoduchou funkcí určenou pro součet dvou celých čísel. Tu lze zapsat takto:
func add(x int, y int) int { return x + y }
Pokud budeme chtít tuto funkci zobecnit, aby sečetla numerické hodnoty libovolného typu a vrátila typově správný výsledek, brzy narazíme. Už jen z toho důvodu, že funkce (jejich názvy) nelze v Go přetěžovat, takže v jednom modulu nemůžeme vytvořit funkci stejného jména, pouze s jinými typy parametrů a návratové hodnoty:
func add(x float32, y float32) float32 { return x + y }
To si ostatně můžeme velmi snadno otestovat:
package main import "fmt" func add(x int, y int) int { return x + y } func add(x float32, y float32) float32 { return x + y } func main() { fmt.Println(add(1, 2)) fmt.Println(add(1.1, 2.2)) }
Pokus o překlad tohoto demonstračního příkladu skončí s chybou:
$ go build add2.go # command-line-arguments ./add2.go:9:6: add redeclared in this block previous declaration at ./add2.go:5:24
11. Příklad omezení současné verze Go
Pro příklady omezení, které nám současná verze programovacího jazyka Go v některých případech klade, nemusíme chodit daleko. Připomeňme si například články o frameworku Gonum, který do určité míry reflektuje možnosti balíčku Numpy pro Python. Zatímco v Numpy lze pracovat s vektory a maticemi, jejichž prvky jsou různých typů, v balíčcích Gonum je tomu jinak – zde se primárně pracuje s prvky typu float64, a to i v případech, kdy by z různých důvodů postačovalo použít prvky float32 nebo naopak complex64, popř. complex128.
Takto se pracuje s vektory, jejichž prvky jsou striktně omezeny na typ float64:
package main import ( "fmt" "gonum.org/v1/gonum/mat" ) func main() { v1 := mat.NewVecDense(5, nil) v2 := mat.NewVecDense(5, []float64{1, 0, 2, 0, 3}) fmt.Printf("dot(v1, v1): %f\n", mat.Dot(v1, v1)) fmt.Printf("dot(v1, v2): %f\n", mat.Dot(v1, v2)) fmt.Printf("dot(v2, v2): %f\n", mat.Dot(v2, v2)) fmt.Printf("max(v2): %f\n", mat.Max(v2)) fmt.Printf("min(v2): %f\n", mat.Min(v2)) fmt.Printf("sum(v2): %f\n", mat.Sum(v2)) }
A takto s maticemi:
package main import ( "fmt" "gonum.org/v1/gonum/mat" ) func main() { d := mat.NewDiagDense(10, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) fmt.Printf("Value:\n%v\n\n", mat.Formatted(d)) d.SetDiag(1, 100) fmt.Printf("Value:\n%v\n\n", mat.Formatted(d)) }
12. Projekt Genny
Jak již víme, generické datové typy ani generické funkce nejsou jazykem Go přímo podporovány. Dokonce ani nemůžeme použít systém maker, protože ten Go neobsahuje. Zbývá nám tedy poslední možnost – nahradit makra a jejich expanze externím nástrojem, který (v ideálním případě) přečte korektní zdrojový kód naprogramovaný v Go a vygeneruje nový kód, v němž dojde k náhradě generického typu za konkrétní datový typ nebo typy.
Tento nástroj se jmenuje Genny a nainstalovat ho můžeme následujícím způsobem:
go get github.com/cheekybits/genny
Po instalaci by měl být k dispozici příkaz genny:
$ genny usage: genny [{flags}] gen "{types}" gen - generates type specific code from generic code. get <package/file> - fetch a generic template from the online library and gen it. {flags} - (optional) Command line flags (see below) {types} - (required) Specific types for each generic type in the source {types} format: {generic}={specific}[,another][ {generic2}={specific2}] Examples: Generic=Specific Generic1=Specific1 Generic2=Specific2 Generic1=Specific1,Specific2 Generic2=Specific3,Specific4 Flags: -in string file to parse instead of stdin -out string file to save output to instead of stdout -pkg string package name for generated files
Tento nástroj nám umožňuje například z následujícího kódu (obsahujícího generický typ):
type NumberType generic.Number func NumberTypeMax(a, b NumberType) NumberType { if a > b { return a } return b }
Vygenerovat například:
package numbers func IntMax(a, b int) int { if a > b { return a } return b }
nebo:
package numbers func Float64Max(a, b float64) float64 { if a > b { return a } return b }
atd.
K dispozici jsou dva generické datové typy, jejichž výchozí typy (před expanzí) vypadají takto:
// Type is the placeholder type that indicates a generic value. // When genny is executed, variables of this type will be replaced with // references to the specific types. // var GenericType generic.Type type Type interface{} // Number is the placehoder type that indiccates a generic numerical value. // When genny is executed, variables of this type will be replaced with // references to the specific types. // var GenericType generic.Number type Number float64
13. Vygenerování kódu pro funkci pro součet dvou čísel projektem Genny
Ukažme si nyní základní vlastnosti nástroje Genny na velmi jednoduchém příkladu – na zobecněné funkci pro součet libovolných numerických hodnot. Celý podbalíček s touto funkcí může vypadat následovně (nový datový typ je vyžadován, samozřejmě se může jmenovat jinak než NumberType):
package adder import "github.com/cheekybits/genny/generic" type NumberType generic.Number func Add(x NumberType, y NumberType) NumberType { return x + y }
Výsledek budeme chtít otestovat takto:
package main import "fmt" import "add3/adder" func main() { fmt.Println(adder.Add(1, 2)) fmt.Println(adder.Add(1.1, 2.2)) }
Nyní si můžeme nechat vygenerovat variantu funkce Add pro všechny numerické datové typy:
$ cat adder.go | genny gen "NumberType=NUMBERS" > adder_generic.go
S tímto výsledkem:
// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package adder func Add(x float32, y float32) float32 { return x + y } func Add(x float64, y float64) float64 { return x + y } func Add(x int, y int) int { return x + y } func Add(x int16, y int16) int16 { return x + y } func Add(x int32, y int32) int32 { return x + y } func Add(x int64, y int64) int64 { return x + y } func Add(x int8, y int8) int8 { return x + y } func Add(x uint, y uint) uint { return x + y } func Add(x uint16, y uint16) uint16 { return x + y } func Add(x uint32, y uint32) uint32 { return x + y } func Add(x uint64, y uint64) uint64 { return x + y } func Add(x uint8, y uint8) uint8 { return x + y }
14. Změna názvu funkce s generickým numerickým datovým typem
Zdrojový kód, který bude transformován nástrojem Genny, je tedy nutné upravit, a to takovým způsobem, že jméno generického datového typu (NumberType) vložíme i do názvu funkce:
package adder import "github.com/cheekybits/genny/generic" type NumberType generic.Number func NumberTypeAdd(x NumberType, y NumberType) NumberType { return x + y }
Nyní bude kód vygenerovaný nástrojem Genny vypadat odlišně – především bude přeložitelný a bude obsahovat všechny typové kontroly:
// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package adder func Float32Add(x float32, y float32) float32 { return x + y } func Float64Add(x float64, y float64) float64 { return x + y } func IntAdd(x int, y int) int { return x + y } func Int16Add(x int16, y int16) int16 { return x + y } func Int32Add(x int32, y int32) int32 { return x + y } func Int64Add(x int64, y int64) int64 { return x + y } func Int8Add(x int8, y int8) int8 { return x + y } func UintAdd(x uint, y uint) uint { return x + y } func Uint16Add(x uint16, y uint16) uint16 { return x + y } func Uint32Add(x uint32, y uint32) uint32 { return x + y } func Uint64Add(x uint64, y uint64) uint64 { return x + y } func Uint8Add(x uint8, y uint8) uint8 { return x + y }
15. Vygenerování testů generické funkce pro součet
I testy pro generickou funkci pro součet lze vygenerovat. Opět si to ukažme na příkladu. Nejprve samotná deklarace generického datového typu a generické funkce:
package adder import "github.com/cheekybits/genny/generic" type NumberType generic.Number func NumberTypeAdd(x NumberType, y NumberType) NumberType { return x + y }
Hlavní modul s testem – funkcí CheckNumberTypeAdd. Povšimněte si, že se ve jméně této funkce opět objevuje jméno generického datového typu:
package main import "fmt" import "add5/adder" func CheckNumberTypeAdd() { var x NumberType x = adder.NumberTypeAdd(1, 2) fmt.Println(x) } func main() { CheckNumberTypeAdd() }
Vygenerovaná podoba testů – zde se opakuje deklarace funkce main a program je tedy nepřeložitelný:
// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package main import ( "add5/adder" "fmt" ) func CheckFloat32Add() { var x float32 x = adder.Float32Add(1, 2) fmt.Println(x) } func main() { CheckFloat32Add() } func CheckFloat64Add() { var x float64 x = adder.Float64Add(1, 2) fmt.Println(x) } func main() { CheckFloat64Add() } func CheckIntAdd() { var x int x = adder.IntAdd(1, 2) fmt.Println(x) } func main() { CheckIntAdd() } func CheckInt16Add() { var x int16 x = adder.Int16Add(1, 2) fmt.Println(x) } func main() { CheckInt16Add() } func CheckInt32Add() { var x int32 x = adder.Int32Add(1, 2) fmt.Println(x) } func main() { CheckInt32Add() } func CheckInt64Add() { var x int64 x = adder.Int64Add(1, 2) fmt.Println(x) } func main() { CheckInt64Add() } func CheckInt8Add() { var x int8 x = adder.Int8Add(1, 2) fmt.Println(x) } func main() { CheckInt8Add() } func CheckUintAdd() { var x uint x = adder.UintAdd(1, 2) fmt.Println(x) } func main() { CheckUintAdd() } func CheckUint16Add() { var x uint16 x = adder.Uint16Add(1, 2) fmt.Println(x) } func main() { CheckUint16Add() } func CheckUint32Add() { var x uint32 x = adder.Uint32Add(1, 2) fmt.Println(x) } func main() { CheckUint32Add() } func CheckUint64Add() { var x uint64 x = adder.Uint64Add(1, 2) fmt.Println(x) } func main() { CheckUint64Add() } func CheckUint8Add() { var x uint8 x = adder.Uint8Add(1, 2) fmt.Println(x) } func main() { CheckUint8Add() }
Je tedy vyžadována ruční úprava!:
// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package main import ( "add5/adder" "fmt" ) func CheckFloat32Add() { var x float32 x = adder.Float32Add(1, 2) fmt.Println(x) } func CheckFloat64Add() { var x float64 x = adder.Float64Add(1, 2) fmt.Println(x) } func CheckIntAdd() { var x int x = adder.IntAdd(1, 2) fmt.Println(x) } func CheckInt16Add() { var x int16 x = adder.Int16Add(1, 2) fmt.Println(x) } func CheckInt32Add() { var x int32 x = adder.Int32Add(1, 2) fmt.Println(x) } func CheckInt64Add() { var x int64 x = adder.Int64Add(1, 2) fmt.Println(x) } func CheckInt8Add() { var x int8 x = adder.Int8Add(1, 2) fmt.Println(x) } func CheckUintAdd() { var x uint x = adder.UintAdd(1, 2) fmt.Println(x) } func CheckUint16Add() { var x uint16 x = adder.Uint16Add(1, 2) fmt.Println(x) } func CheckUint32Add() { var x uint32 x = adder.Uint32Add(1, 2) fmt.Println(x) } func CheckUint64Add() { var x uint64 x = adder.Uint64Add(1, 2) fmt.Println(x) } func CheckUint8Add() { var x uint8 x = adder.Uint8Add(1, 2) fmt.Println(x) } func main() { CheckIntAdd() CheckInt8Add() CheckInt16Add() CheckInt32Add() CheckInt64Add() CheckUintAdd() CheckUint8Add() CheckUint16Add() CheckUint32Add() CheckUint64Add() CheckFloat32Add() CheckFloat64Add() }
16. Anonymní funkce v expandovaném kódu
Předchozí příklad byl dosti umělý, ovšem ukažme si praktičtější zadání, tentokrát s anonymní funkcí, která bude implementovat porovnání dvou numerických hodnot. Na základě výsledku porovnání se vrátí buď první hodnota nebo hodnota druhá.
Implementace může vypadat následovně:
package math import "github.com/cheekybits/genny/generic" type ThisNumberType generic.Number func ThisNumberTypeMax(fn func(a, b ThisNumberType) bool, a, b ThisNumberType) ThisNumberType { if fn(a, b) { return a } return b }
A expanze nástroje Genny pro všechny numerické datové typy:
// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package math func Float32Max(fn func(a, b float32) bool, a, b float32) float32 { if fn(a, b) { return a } return b } func Float64Max(fn func(a, b float64) bool, a, b float64) float64 { if fn(a, b) { return a } return b } func IntMax(fn func(a, b int) bool, a, b int) int { if fn(a, b) { return a } return b } func Int16Max(fn func(a, b int16) bool, a, b int16) int16 { if fn(a, b) { return a } return b } func Int32Max(fn func(a, b int32) bool, a, b int32) int32 { if fn(a, b) { return a } return b } func Int64Max(fn func(a, b int64) bool, a, b int64) int64 { if fn(a, b) { return a } return b } func Int8Max(fn func(a, b int8) bool, a, b int8) int8 { if fn(a, b) { return a } return b } func UintMax(fn func(a, b uint) bool, a, b uint) uint { if fn(a, b) { return a } return b } func Uint16Max(fn func(a, b uint16) bool, a, b uint16) uint16 { if fn(a, b) { return a } return b } func Uint32Max(fn func(a, b uint32) bool, a, b uint32) uint32 { if fn(a, b) { return a } return b } func Uint64Max(fn func(a, b uint64) bool, a, b uint64) uint64 { if fn(a, b) { return a } return b } func Uint8Max(fn func(a, b uint8) bool, a, b uint8) uint8 { if fn(a, b) { return a } return b }
17. Negenerický binární strom (s konkrétními typy hodnot uzlů)
Plná síla nástroje Genny se projeví při implementaci různých kontejnerů (seznamů, zásobníků, front, stromů, grafů atd.). Ukažme si nejprve implementaci binárního stromu, který je negenerický, tj. jeho uzly mají předem daný konkrétní typ hodnot uzlů:
package main import ( "fmt" ) type Item int type Node struct { Value Item Left *Node Right *Node } type BinaryTree struct { Root *Node } func (bt *BinaryTree) Insert(value Item) { node := &Node{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertNode(bt.Root, node) } } func insertNode(node, newNode *Node) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertNode(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertNode(node.Right, newNode) } } } func printTree(node *Node, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printTree(node.Left, level) fmt.Printf(format+"%v\n", node.Value) printTree(node.Right, level) } } func main() { var bt BinaryTree bt.Insert(8) bt.Insert(3) bt.Insert(11) bt.Insert(1) bt.Insert(0) bt.Insert(2) bt.Insert(5) bt.Insert(4) bt.Insert(6) bt.Insert(9) bt.Insert(8) bt.Insert(10) bt.Insert(13) bt.Insert(12) bt.Insert(14) printTree(bt.Root, 0) }
Tento příklad je plně funkční – vytvoří vyvážený binární strom s patnácti uzly:
---[ 0 ---[ 1 ---[ 2 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 8 ---[ 8 ---[ 9 ---[ 10 ---[ 11 ---[ 12 ---[ 13 ---[ 14
18. Generický binární strom
Zkusme si nyní předchozí příklad přepsat takovým způsobem, aby byl binární strom generický, tj. aby ho bylo možné použít pro různé typy uzlů a přitom se zachovala typová bezpečnost:
package main import ( "fmt" "github.com/cheekybits/genny/generic" ) type Item generic.Number type ItemNode struct { Value Item Left *ItemNode Right *ItemNode } type ItemBinaryTree struct { Root *ItemNode } func (bt *ItemBinaryTree) Insert(value Item) { node := &ItemNode{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertItemNode(bt.Root, node) } } func insertItemNode(node, newNode *ItemNode) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertItemNode(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertItemNode(node.Right, newNode) } } } func printItemTree(node *ItemNode, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printItemTree(node.Left, level) fmt.Printf(format+"%v\n", node.Value) printItemTree(node.Right, level) } }
Vygenerovat si můžeme například variantu pro hodnoty typu float32:
$ cat binary_tree_generic.go | genny gen "Item=float32 > b.go
S výsledkem:
// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package main import "fmt" type Float32Node struct { Value float32 Left *Float32Node Right *Float32Node } type Float32BinaryTree struct { Root *Float32Node } func (bt *Float32BinaryTree) Insert(value float32) { node := &Float32Node{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertFloat32Node(bt.Root, node) } } func insertFloat32Node(node, newNode *Float32Node) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertFloat32Node(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertFloat32Node(node.Right, newNode) } } } func printFloat32Tree(node *Float32Node, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printFloat32Tree(node.Left, level) fmt.Printf(format+"%v\n", node.Value) printFloat32Tree(node.Right, level) } }
Výsledek při volbě typu Int64:
type Int64Node struct { Value int64 Left *Int64Node Right *Int64Node } type Int64BinaryTree struct { Root *Int64Node } func (bt *Int64BinaryTree) Insert(value int64) { node := &Int64Node{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertInt64Node(bt.Root, node) } } func insertInt64Node(node, newNode *Int64Node) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertInt64Node(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertInt64Node(node.Right, newNode) } } } func printInt64Tree(node *Int64Node, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printInt64Tree(node.Left, level) fmt.Printf(format+"%v\n", node.Value) printInt64Tree(node.Right, level) } }
A konečně – nemusí se jednat o binární strom, jehož prvky jsou čísla. Vyzkoušejme si variantu s řetězci:
// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package main import "fmt" type StringNode struct { Value string Left *StringNode Right *StringNode } type StringBinaryTree struct { Root *StringNode } func (bt *StringBinaryTree) Insert(value string) { node := &StringNode{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertStringNode(bt.Root, node) } } func insertStringNode(node, newNode *StringNode) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertStringNode(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertStringNode(node.Right, newNode) } } } func printStringTree(node *StringNode, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printStringTree(node.Left, level) fmt.Printf(format+"%v\n", node.Value) printStringTree(node.Right, level) } } func main() { var bt StringBinaryTree bt.Insert("8") bt.Insert("3") bt.Insert("11") bt.Insert("1") bt.Insert("0") bt.Insert("2") bt.Insert("5") bt.Insert("4") bt.Insert("6") bt.Insert("9") bt.Insert("8") bt.Insert("10") bt.Insert("13") bt.Insert("12") bt.Insert("14") printStringTree(bt.Root, 0) }
---[ 0 ---[ 1 ---[ 10 ---[ 11 ---[ 12 ---[ 13 ---[ 14 ---[ 2 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 8 ---[ 8 ---[ 9
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně šest až sedm megabajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- Go Data Structures: Binary Search Tree
https://flaviocopes.com/golang-data-structure-binary-search-tree/ - Gobs of data
https://blog.golang.org/gobs-of-data - Formát BSON
http://bsonspec.org/ - Golang Guide: A List of Top Golang Frameworks, IDEs & Tools
https://blog.intelligentbee.com/2017/08/14/golang-guide-list-top-golang-frameworks-ides-tools/ - Tvorba univerzálních projevů
http://www.kyblsoft.cz/projevy - Repositář projektu Gift
https://github.com/disintegration/gift - Dokumentace k projektu Gift
https://godoc.org/github.com/disintegration/gift - Online x86 / x64 Assembler and Disassembler
https://defuse.ca/online-x86-assembler.htm#disassembly2 - The Design of the Go Assembler
https://talks.golang.org/2016/asm.slide#1 - A Quick Guide to Go's Assembler
https://golang.org/doc/asm - AssemblyPolicy
https://github.com/golang/go/wiki/AssemblyPolicy - Geohash in Golang Assembly
https://mmcloughlin.com/posts/geohash-assembly - Command objdump
https://golang.org/cmd/objdump/ - Assembly
https://goroutines.com/asm - Go & Assembly
http://www.doxsey.net/blog/go-and-assembly - A Foray Into Go Assembly Programming
https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/ - Golang Capturing log.Println And fmt.Println Output
https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4 - Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - Michael Fogleman's projects
https://www.michaelfogleman.com/projects/tagged/graphics/ - Color Graphs of Complex Functions
https://web.archive.org/web/20120511021419/http://w.american.edu/cas/mathstat/lcrone/ComplexPlot.html - A Gallery of Complex Functions
http://wismuth.com/complex/gallery.html - package glot
https://godoc.org/github.com/Arafatk/glot - Gnuplotting: Output terminals
http://www.gnuplotting.org/output-terminals/ - Introducing Glot the plotting library for Golang
https://medium.com/@Arafat./introducing-glot-the-plotting-library-for-golang-3133399948a1 - Introducing Glot the plotting library for Golang
https://blog.gopheracademy.com/advent-2018/introducing-glot/ - Glot is a plotting library for Golang built on top of gnuplot
https://github.com/Arafatk/glot - Example plots (gonum/plot)
https://github.com/gonum/plot/wiki/Example-plots - A repository for plotting and visualizing data (gonum/plot)
https://github.com/gonum/plot - golang library to make https://chartjs.org/ plots
https://github.com/brentp/go-chartjs - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Stránky projektu MinIO
https://min.io/ - MinIO Quickstart Guide
https://docs.min.io/docs/minio-quickstart-guide.html - MinIO Go Client API Reference
https://docs.min.io/docs/golang-client-api-reference - MinIO Python Client API Reference
https://docs.min.io/docs/python-client-api-reference.html - Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/ - Benchmarking MinIO vs. AWS S3 for Apache Spark
https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/ - MinIO Client Quickstart Guide
https://docs.min.io/docs/minio-client-quickstart-guide.html - Analýza kvality zdrojových kódů Minia
https://goreportcard.com/report/github.com/minio/minio - This is MinIO
https://www.youtube.com/watch?v=vF0lQh0XOCs - Running MinIO Standalone
https://www.youtube.com/watch?v=dIQsPCHvHoM - „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
https://www.youtube.com/watch?v=wlpn8K0jJ4U - Ginkgo
http://onsi.github.io/ginkgo/ - Gomega
https://onsi.github.io/gomega/ - Ginkgo's Preferred Matcher Library na GitHubu
https://github.com/onsi/gomega/ - Provided Matchers
http://onsi.github.io/gomega/#provided-matchers - Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - Go is on a Trajectory to Become the Next Enterprise Programming Language
https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 10 tools written in Go that every developer needs to know
https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 5 simple tips and tricks for writing unit tests in #golang
https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - How the Go runtime implements maps efficiently (without generics)
https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/ - 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go