Portál AbcLinuxu, 10. května 2024 05:21

Google Go – 1. narozeniny

13. 1. 2011 | Jan Mercl, Ondřej Surý
Články - Google Go – 1. narozeniny  

„Do not communicate by sharing memory; instead, share memory by communicating“ – zahajujeme seriál o programovacím jazyku Go.

Obsah

Společnost Google představila veřejně svůj jazyk Go o trochu víc než před rokem, v listopadu 2009. Mezi autory jazyka patří např. Rob Pike nebo „otec zakladatel“ Unixu Ken Thompson. Na oficiální domovské stránce jazyka Go je k nalezení mnoho užitečných informací jako třeba několik různě pokročilých tutoriálů, odkazy na prezentace a blog, kompletní formální specifikace jazyka, apod.

U oné specifikace je snad zajímavé poznamenat, že se vejde na sice trochu delší, ale přece jen jednu webovou stránku. Nejde ani tolik o rozsah, ale spíše o to, že specifikace Go, což není úplně obvyklé, je určena pro čtení a studium programátora a nahrazuje tak i něco ve stylu „referenční příručky jazyka“, která u jiných programovacích jazyků typicky nemá, či obvykle nemívá, úplnost specifikace. Z uvedeného plyne téměř nutně, že definice syntaxe i sémantiky jazyka Go je poměrně pravidelná, obsahuje jen málo základních konceptů, které jsou ale navrženy tak, aby při kompozici složitějších, leč často se vyskytujících konstrukcí, pokud možno žádný podstatný nechyběl.

Pokud navštívíte výše zmiňovanou domovskou stránku Go, tak v tom momentě ve skutečnosti využíváte aplikaci godoc, která je určena pro práci s dokumentací zdrojových kódů Go z příkazové řádky, ale umí se chovat i jako i webový server. Přestože jeden z autorů jazyka loni potvrdil, že Go se v rámci společnosti Google používá pro opravdové nasazení, je webový server Go domovské stránky jedinou takovou konkrétně potvrzenou a známou aplikací.

Následující text, dnes jen jeho první část, je míněn jako letmé shrnutí pro programátory a částečně jako snad užitečný rozcestník. Podrobnější informace, ukázky kódu atd. viz odkazy na domovské stránce Go.

Přehled některých vybraných vlastností jazyka Go

link

Kategorizace

link

Go je imperativní (neboli též procedurální) programovací jazyk, podobně jako třeba C, Java, Pascal, Basic, ... Program v Go je sadou příkazů, které mění stav procesu/aplikace. Na opačné straně pomyslné hranice tohoto rozdělení stojí více či méně čistě funkční programovací jazyky z nichž mezi ty dobře známé patří např. LISP.

Logika rozhodování, která se používá třeba u příkazů if, for, je standardní booleovská, tedy dvouhodnotová a Go se zde opět drží v tradičních mezích výše uvedené rodiny typických procedurálních jazyků.

Hodnoty s typem funkce (ukazatel na funkci) jsou v Go objekty první kategorie:

Do Go by díky tomu nemělo být zas až tak těžké přepsat implementaci některých algoritmů, které jsou ve funkčních programovacích jazycích někdy mnohem kratší a/nebo elegantnější v zápisu.

Syntaxe

link

Gramatika jazyka Go je navržena s cílem minimalizovat kód a maximalizovat jeho čitelnost (to druhé je samozřejmě z definice čistě subjektivní kategorie, přesto vděčný zdroj četných flame wars). Součástí distribuce je nástroj pro standardní formátování kódu: http://golang.org/cmd/gofmt/ a http://research.swtch.com/2009/12/gofmt.html.

Bloková struktura, tj. příkaz sestávající z libovolného počtu jiných příkazů, je stejná jako v C nebo Javě, tedy je uzavřena ve složených závorkách. Příkazy se ukončují středníkem (opět jako v C). Určitě příjemné pro programátora je, že v naprosté většině míst lze ukončovací středník ze zápisu vypustit. Jistou daní za takové pohodlí je nutnost dodržení některých konvencí. Odměnou za jejich respektování je pak to, že kompilátor automaticky vloží středníky na správná místa.

Deklarace typů nejsou ve stylu C. Používají se sice C ukazatelové konvence (*) avšak s pořadím převážně jako v Pascalu a dalších jazyků z dílny profesora N. Wirtha. Důvodem je i v tomto případě zlepšení čitelnosti. Ano, jistě je to opět subjektivní záležitost, ale tentokráte podpořená i trochou teorie. Byla by jistě škoda na konci právě odkazovaného článku přehlédnout jeho úplně poslední odkaz. Tam se totiž dozvíte, proč se programátorovi při čtení deklarací typů v Go hlava nezatočí. Doufám, že mi laskaví příznivci jazyka C prominou.

Použitelnost oficiální distribuce

link

Vývojářský tým Go uvolňuje nová vydání obvykle každý týden nebo jednou za dva týdny a vydané verze lze označit za docela stabilní. Množství nacházených chyb je myslím přiměřené rozsahu projektu, jejich odstraňování bývá docela rychlé. Zázemí velké společnosti se na vlastnostech vývoje tohoto open source projektu projevuje nesporně příznivě.

Přesto Go ještě není určeno pro produkční nasazení. Důvodem je jiná nestabilita a to jazyka jako takového. Při svém výslovně uváděném a zdůrazňovaném experimentálním statusu si autoři Go zatím mohou dovolit zasahovat do definice jazyka i způsoby, které nejsou zpětně kompatibilní s existujícím kódem třetích stran. V silně aktivní diskuzní skupině Go se návrhy na některé úpravy probírají prakticky denně a autoři jazyka občas výslovně uvádějí úmysl a/nebo ochotu měnit jej k lepšímu i za cenu „rozbití“ staršího kódu, pokud nabudou přesvědčení o účelnosti takové změny. K odhadem asi minimálně tuctu takových změn již za krátkou historii jazyka i skutečně došlo.

Asi nejvýznamnější zpětně nekompatibilní změnou jazyka Go bylo opuštění původní zcela volné podoby (free form) zápisu kódu. Bylo to ovšem výměnou za novou možnost nepsat téměř žádné středníky ukončující příkazy (viz předchozí kapitola „Syntaxe“). Free form zápis, tak jak je znám u jazyků C, Java nebo třeba Pascal, dovoluje libovolně formátovat kód pomocí oddělujících „bílých“ znaků. Typicky to jsou mezery, tabulátory, konce řádků, ale také komentáře, pokud nemají hodnotu z pohledu sémantiky programu.

Změna směrem k omezení úplně volné formy byla velká a úměrné tomu byly zpočátku i vášně většiny lidí o ní diskutujících. Z „historického“ pohledu je docela zajímavý první příspěvek Roba Pikea ve vláknu, ve kterém ohlašuje úmysl vývojářů změnu uskutečnit a přidává podrobný pohled na to, co všechno se tím změní. Jazyk kupříkladu přišel o možnost automatického spojování sousedících řetězců (tak jako v C). To dle debat nevadilo skoro nikomu. Nadále už také nebylo dle návrhu možné rozdělovat (dlouhé) výrazy s binárními operátory kdekoli, ale pouze tak, že operátor v takovém případě musí „viset“ na konci řádku. Pokud by jím, jak bylo dříve možné, začínal až řádek následující, opět by došlo k automatickému vložení ukončovacího středníku a původní kód by nešel dále kompilovat. Nejhorší možný případ by ovšem byl, pokud by stávající kód i nadále kompilovat šel, jen s tichou změnou sémantiky programu. I tuto katastrofickou variantu Rob ve svém návrhu bere do úvahy. Ale ani „nesvobodné“ umísťování operátorů, čárek oddělujících položky seznamu apod. nedokázaly dohromady vyvolat a dodnes občas vyvolávat bouře. Přitom jde ve skutečnosti opravdu jen o pouhý zvyk. Kdo tuší, že řeč je o „brace style“, tak to odhadl naprosto správně.

V dnešní podobě Go (už) nemáte možnost, napsat otevírací složenou závorku bloku, kterou začíná tělo funkce nebo např. složený příkaz po if kdekoli jinde než na stejném řádku jako je hlavička funkce nebo příkaz. A řekněme, že odhadem polovina z desítky (nebo snad desítek?) běžně se vyskytujících stylů psaní složených závorek je má přitom až na řádce následující. Výsledkem třaskavého mixu těchto dvou skutečností bylo, že někteří členové diskuzní skupiny golang-nuts protestně páchali virtuální, závorkově rituální sebevraždy. Považovali mj. totiž za nezbytně nutné oznámit všem ostatním několika tisícům odběratelům této e‑mailové konference, že s Go kvůli této změně navždy, neodvolatelně a do smrti končí. R.I.P.

Pro otrlé zvědavce ukázka mezní (ze strany odpůrce nových omezujících pravidel) vyhrocenosti debaty na vývojářské konferenci mezi autory jazyka a autorem implementace navrhované změny, která měla opětně dovolit psát alespoň částečně free form Go.

Cílové systémy/architektury, instalace ze zdrojových kódů, externí kód

link

Pro překlad jazyka Go jsou k dispozici různé kompilátory, pojmenované dle konvencí Plan9, tj. podle cílové instrukční sady procesoru – 5g (ARM), 6g(AMD64) a 8g(386), souhrnně se jim říká také „gc“. K nim příslušejí další nástroje (linkovací programy 5l, 6l, 8l, assemblery, 5a, 6a, 8a atd.). Kromě požadovaného procesoru samozřejmě kompilátor ještě musí vědět na kterém operačním systému má být vytvořená binární aplikace spouštěna. Oficiální distribuce Go podporuje zatím tyto kombinace OS a procesoru:

Používání knihoven vytvořených jinými kompilátory v C ABI umožňuje nástroj cgo. Je možné z Go volat funkce C knihoven a možná jsou i zpětná volání Go funkcí z C knihovny (callbacks). Tato obousměrná interakce je trochu ztížena přepínáním zásobníků a vláken na hranici Go a C světů, takže se hodí nejlépe tam, kde C kód vykonává relativně hodně činnosti v porovnání s ne zcela zanedbatelnou režií přepnutí. Vhodný příklad je třeba využití libgtk, nevhodným je pak jakákoli často volaná lineárně prováděná C funkce v rozsahu pár desítek řádků.

Postup pro získání celého Go zdrojového stromu, sestavení a provoz uvedených kompilátorů, resp. celého řetězce „gc“ nástrojů je na webu golang.org.

Další implementací kompilátoru jazyka Go je gccgo, což je nový Go frontend pro GNU gcc v blížícím se vydání 4.6. Gccgo umožňuje za určitých okolností přímou vazbu deklarací a přímé volání mezi Go a C kódem, tedy s menší režií než „gc“ kompilátory. Bližší podrobnosti k této problematice jsou uvedeny v sekci „C Interoperability“ návodu pro získání gccgo zdrojové větve gcc, sestavení a provoz gccgo.

Gccgo překladem Go zdrojového kódu do vnitřní reprezentace gcc získává pro Go potenciálně všechny cílové platformy gcc. To byla ta dobrá zpráva. Na druhé straně se za to zatím platí třeba i tím, že kód z gccgo používá pro gorutiny NPTL vlákna. Hodí se tedy lépe pro problémy, které jsou implementovány s omezeným počtem gorutin. Vlákna mají o hodně větší nároky na paměť i na přepnutí než gorutiny, podrobněji se o tom dozvíte v druhé části článku.

Kompilace vs. interpretace

link

Jazyk svým návrhem nijak nebrání implementaci obou možností. Naopak by se dalo říct, že v obou případech jsou některé vlastnosti Go výhodné. Např.:

Implementace poskytovaná společností Google je kompilovaná do nativního kódu se statickým linkováním (přinejmenším v případě „gc“ kompilátorů 6g,...). Součástí distribuce je také modul interpretru, který ale ještě neimplementuje celý jazyk. Jak je možné, že po roce od prvního vydání (modul eval je v Go od prvního vydání) je tento modul stále ještě nehotový? O pár řádků výše se hovoří o nepřítomnosti direktiv, maker a podobných konstrukcí v Go. Nepřímý důsledek toho je, že v Go tedy neexistují a existovat ani nemohou žádné hlavičkové soubory (dívám se na tebe, Cčko). Neexistují-li hlavičkové soubory, je pohodlná cesta pro modulární kompilaci přes přítomnost exportovaných definicí v objektovém kódu (v angličtině „object code“, který nemá nic společného s objektovým programováním, viz též Wikipedii). To není žádná novinka, třeba Delphi se svými .dcu soubory dělá přesné totéž už hezkou řádku let. Odměnou za definice, jejichž import při kompilaci nevyžaduje syntaktickou analýzu textu, případné opětné vytváření AST a interpretaci jeho sémantiky, je rychlost kompilace.

Pokud v Go program A importuje modul B, který importuje modul C, pak zkompilovaný objektový kód modulu C obsahuje všechny exportované entity modulu C. To je zatím celkem standardní záležitost. Zkompilovaný objektový kód modulu B obsahuje totéž pro B, ale i všechny entity z C, které jsou přes B také viditelné. Například v B je exportovaná funkce, která vrací hodnotu typu, který je exportován z C. Program A nemusí C importovat vůbec, ale při kompilaci A, se objektový kód C použije až pro sestavení/linkování. Pro vytvoření objektového kódu A není objektový kód C potřeba (navíc si představme A jako levou rekurzi celé situace). Vynechány teď byly některé implementační detaily, které kontrolují třeba verzování apod., protože se dostáváme k důležitému poznatku. Pokud kompilujeme modul/program, který importuje N jiných modulů, kompilátor potřebuje: a) jen binární resp. strojově formátovaná data b) jen z N dalších modulů. Například v C a C++ jde přitom o N kořenů stromů závislostí na dalších zdrojových souborech pomocí #include a v nich dále vnořených #include atd. Náročnost zpracování takového stromu při kompilaci může snadno růst exponenciálně s velikostí projektu. V Go nejde o strom, ale o vektor a je zaručen nejvýše lineární růst složitosti kompilace.

Tím se konečně dostáváme k odpovědi na otázku o pár odstavců výše. Typická doba kompilace Go modulu na obyčejném počítači je pod 100 ms, typická doba sestavení Go programu je pod 1 sekundu. Kompilace programátory v Go zdržuje tak málo, že tlak na dokončení oficiálního interpretru je malý. I v hypotetické aplikaci používající Go jako skriptovací jazyk je dobře představitelné Go „skript“ bleskově on-demand zkompilovat před jeho „interpretací“.

Závěr této části

link

Dnešní téma bylo větším dílem pohled na Go jako takové a jeho konkrétní implementace. V příští části se zase naopak dostaneme více k přehledu o samotnému programování v Go, tedy například jaké typy, hodnoty a konstrukce s ním lze vytvářet, co by na něm kodéra asi mělo nebo možná mohlo zaujmout. Například co chtěl básník říci sloganem v záhlaví článku.

Laboratoře CZ.NIC

O autorech

Jan Mercl a Ondřej Surý pracují v Laboratořích CZ.NIC, výzkumném a vývojovém centru správce české národní domény.

Seriál Google Go (dílů: 8)

První díl: Google Go – 1. narozeniny, poslední díl: Google Go – 2. narozeniny.
Následující díl: Google Go – co najdeme ve stavebnici

Další články z této rubriky

LLVM a Clang – více než dobrá náhrada za GCC
Ze 4 s na 0,9 s – programovací jazyk Vala v praxi
Reverzujeme ovladače pro USB HID zařízení
Linux: systémové volání splice()
Programování v jazyce Vala - základní prvky jazyka

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.