Hlavní navigace

Squeak: návrat do budoucnosti (3)

Pavel Křivánek

Dnes se konečně podíváme na jazyk Smalltalk jako takový. Probereme si jeho syntaxi, a abychom to moc neroztahovali, tak celou.

Smalltalk je čistě objektově orientovaný inkrementální jazyk. Je vystavěn na velmi obecných základech, což mu dodává nebývalou mocnost a pružnost. Od začátku byl koncipován s ohledem na co nejlepší čitelnost a srozumitelnost zdrojových kódů v něm napsaných. Smalltalk je case-sensitive, bere tedy ohled na velikost jednotlivých písmen. Ač vychází částečně z Lispu, rozhodně nečekejte žádné závorkové peklo. Syntaxe Smalltalku sice vypadá na první pohled poněkud nezvykle, ovšem má svoji jasnou a striktně dodržovanou logiku.

Smalltalk zná vlastně jen tři operace. Je to zaslání zprávy objektu, specifikace objektu a vrácení objektu jako výsledku volání zprávy. Skoro všechno, co ve smalltalkovském kódu můžete vidět, je voláním zprávy nějakého objektu. Nejdříve určíme objekt, kterému máme zprávu zaslat, a poté identifikátor zprávy. Zápis pak vypadá takto: object message

Například pokud napíšeme 1234 factorial, zašleme objektu reprezentujícímu číslo 1234 zprávu jménem factorial. Výsledkem pak bude objekt reprezentující faktoriál čísla 1234. Takovýmto zprávám říkáme unární zprávy, mají nejvyšší prioritu a vyhodnocují se zleva doprava. Pokud napíšeme 1234 factorial asString size, dopracujeme se stejného výsledku, jako kdybychom napsali ((1234 factorial) asString) size. Tento výraz nejdříve vypočítá faktoriál čísla 1234, potom výsledek převede na řetězec voláním zprávy (metody) asString a výslednému řetězci nakonec pošle zprávu size, která vrátí jeho délku. Celkovým výsledkem pak je objekt reprezentující číslo 3281, což je počet číslic faktoriálu čísla 1234.

Můžete se zeptat, kde se objekt čísla 1234 vlastně vzal. Odpověď nám dá letmý pohled do přeloženého bytekódu (jeho slovní reprezentace).

pushConstant: 1234
send: factorial
pop

Interpret vezme konstantní výraz, který jste napsali do zdrojového kódu, a vytvoří podle něj objekt tento výraz reprezentující – v našem případě objekt třídy SmallInteger. Stejně postupuje i u ostatních konstantních výrazů, takže například ve výrazu 'Hello world' size interpret vytvoří objekt třídy String reprezentující daný řetězec a pošle mu zprávu size na zjištění jeho délky. V bytekódu se pak objeví  pushConstant: 'Hello world'.

Čísla

Konstantní výrazy pro celá čísla zahrnují desítkové číslice ( 1234), osmičkové číslice ( 8r177), šestnáctkové číslice ( 16rFF) a samozřejmě dvojkové číslice ( 2r01011). Písmeno r znamená radix (základ použité číselné soustavy) a budiž poznamenáno, že nejste omezeni jen na tyto základní číselné soustavy. Můžete tak například uvádět čísla v trojkové soustavě ( 3r21 =7) či libovolné jiné ( 25472r21 =50945). Základ je vždy vyjádřen desítkově. Zapomenout nesmíme na celá čísla s exponentem ( 123e2). Celá čísla jsou objekty třídy SmallInteger s rozsahem –230 až 230-1. Pokud již tento rozsah nestačí, dochází k automatické konverzi na instance tříd LargePositive­Integer a LargeNegative­Integer. Ty nemají žádné explicitní velikostní omezení, ale počítání s nimi je podstatně pomalejší.

Reálná čísla jsou instancemi třídy Float. Pro jejich konstantní výrazy platí podobná pravidla jako pro celá čísla ( 3.14, 3.14e-10, 2r1.1). Za pozornost stojí, že operace dělení (např. 10 / 3) nevrací reálné číslo, ale zlomek (třída Fraction). Odpadají tedy všemožné zaokrouhlovací chyby a počítání je naprosto přesné. Mimoto Squeak podporuje ještě počítání s nekonečnem.

Znaky, Řetězce a Symboly

S řetězci jsme se již setkali. Jsou ohraničeny apostrofy a a mohou být i víceřádkové (v takovém případě je znak konce řádku součástí řetězce, nepoužívají se speciální znaky typu \n). Pokud chceme, aby řetězec obsahoval apostrof, vložíme do něj dva apostrofy za sebou (''). Řetězce jsou instancemi třídy String, jsou velmi podobné polím a indexují se od jedničky.

Konstantní výrazy pro znaky začínají symbolem dolaru $, za nímž bez výjimky následuje požadovaný znak. $3 je tedy znak pro číslici 3, $a je znak pro písmeno a, $$ je znak pro znak dolaru, $ je znak pro mezeru. Jsou to instance třídy Character. V případě mezery, konce řádku a podobných znaků se přirozeně doporučuje jiná méně matoucí reprezentace (např. Character space, Character cr apod.).

Symboly (třída Symbol) jsou speciálními druhy řetězců. Liší se v tom, že všechny symboly obsahující stejnou sekvenci znaků mají jednu společnou instanci (rovnost je u nich totožná s identitou). Symboly jsou řetězce uvozené znakem mřížky, např. #'toto je symbol'. Mají široké spektrum použití. Využívají se mimo jiné pro identifikátory zpráv. Při dodržení omezené znakové sady je lze psát bez apostrof (např. #factorial)

Sekvence výrazů a kaskáda

Jednotlivé výrazy v sekvenci výrazů jsou odděleny tečkou. Ta hraje stejnou úlohu jako středník v C/C++. Výsledek sekvence výrazů je roven výsledku posledního výrazu. Poslední tečka je volitelná. Pokud chceme poslat jednomu objektu více zpráv za sebou, můžeme tak učinit buď sekvencí výrazů object message1. object message2. object message3., nebo pomocí kaskády, kde jsou jednotlivé zprávy odděleny středníkem: object message1; message2; message3. Často se na konci kaskády používá zpráva yourself, která vrátí volaný objekt  object.

Pole

Konstantní pole vytváří instance třídy Array. Jsou tvořena konstantami v závorkách za znakem mřížky. Například

#(1 2 3 4 5) je pole velikosti 5 obsahující čísla 1 až 5.
#('this' #is $a #'constant' #array) je pole velikosti pět obsahující řetězec, symbol, znak, symbol a symbol.

V konstantním poli může být obsaženo i další konstantní pole, např. #(1 2 #(3 4)) je pole velikosti tři. Protože konstantní pole obsahuje jen konstanty, je například #(1 + 2) polem o velikosti tři obsahujícím dvě čísla a jeden symbol.

Výrazová pole se od konstantních polí liší v tom, že místo sekvence konstant obsahují sekvenci výrazů. Pole je pak tvořeno výsledky jednotlivých výrazů. Jsou ohraničena složenými závorkami, např. { 1. 2+3. 1234 factorial }

Proměnné

Lokální proměnné jsou pojmenovávány pomocí identifikátorů, což, stejně jako v jiných jazycích, jsou sekvence znaků a číslic začínajících písmenem. Identifikátory lokálních proměnných z konvence začínají malým písmenem, a pokud vyjadřují víceslovné spojení, každé další významové slovo začíná velkým písmenem (např. totoJeIdentifi­kator). Identifikátory nesmí obsahovat podtržítka. Seznam lokálních proměnných musí být vždy na začátku metody nebo bloku a je ohraničen znaky pro rouru

| var1 var2 var3 |

Pokud vám zde něco chybí, třeba určení typů, nehledejte to, nepovedlo by se vám to. Jak jsme si řekli, proměnné ve Smalltalku nelze deklarovat kdekoliv. Nicméně vzhledem k tomu, že se u nich nemusí uvádět typy, dokáže se o ně postarat editor kódu. Pokud narazí na neznámý identifikátor, nabídne vám nejpodobnější známé identifikátory (to pro případ, že jste se přepsali) včetně možnosti deklarace nové lokální proměnné. V takovém případě ji sám rád automaticky doplní do jejich seznamu. Pokud je naopak nějaká lokální proměnná nevyužívána, nabídne vám při rekompilaci metody její odstranění.

Globální proměnné začínají z konvence velkými písmeny. Jsou obsaženy v systémovém slovníku jménem Smalltalk a k jejich popisu se dostaneme později. Poznamenejme, že třídy jsou rovněž globální proměnné.

Mimoto existují ještě instanční proměnné, třídní proměnné a sdílené slovníky, ale ty si rovněž necháme až na některý z následujících dílů.

Pseudo-proměnné

Pseudo-proměnné jsou rezervované identifikátory podobné klíčovým slovům z jiných jazyků. Nelze je použít na levé straně přiřazení. Jsou to:

nil
Obdoba NULL apod. Jedinečná instance třídy UndefinedObject. Neinicializované objekty mají implicitně tuto hodnotu.
true, false
Jedinečné instance tříd True a False.
self
Obdoba „this“ z C++. Jedná se o referenci na objekt, který je příjemcem vykonávané zprávy.
super
Volání metody předka. Referencuje sice stejný objekt jako self, ovšem v kontextu předka jeho třídy.
thisContext
Objekt popisující aktivní kontext vykonávané zprávy nebo bloku. Obsahuje např. zásobník volání a jiné informace používané především při ladění.

Komentáře

Komentáře jsou vkládány do uvozovek a mohou být víceřádkové. Např. "toto je komentář". Uvozovky lze do komentáře vložit jejich zdvojením. Smalltalk bohužel nemá jednořádkové komentáře, tedy nic podobného dvěma lomítkům z C++ a spol.

Zprávy

O unárních zprávách jsme se již zmínili. Nepřijímají žádné argumenty a jsou vyhodnocovány jako první.

Binární zprávy jsou identifikovány binárními identifikátory (+, -, , <= atd.). Jsou vyhodnocovány jako druhé a vždy zleva doprava bez ohledu na prioritu numerických operací. 2 + 3 * 4 je tedy 20. Objektu 2 se pošle binární zpráva #+ s jedním parametrem 3 a výsledkem je objekt čísla 5. Tomu je poslána binární zpráva # s parametrem 4 a výsledkem je číslo 20. Samozřejmě lze použít závorky: 2 + (3 * 4).

Nakonec jsou ve výrazech vyhodnoceny slovní zprávy. Přijímají jeden nebo více argumentů a tvoří je identifikátory ukončené dvojtečkami. Např. array at: 1 put: 'první prvek' pošle zprávu #at:put: s dvěma parametry objektu array (lokální proměnná). 10 perform: #factorial řekne objektu 10, že má vykonat unární zprávu #factorial (stejné jako 10 factorial).

Vyhodnocování zpráv tedy probíhá v pořadí unární zprávy, binární zprávy, slovní zprávy. Výraz array at: 1 + 2 factorial put: 'první prvek' je vyhodnocen jako array at: (1 + (2 factorial)) put: 'první prvek'.

Přiřazení

Pro přiřazení se používají znaky := jako v Pascalu (= je použito pro rovnost, == pro identitu). Původně se používal znak šipky doleva (), který supluje podtržítko (_). S ním se lze stále setkat velmi často. Přiřazení je výraz, a proto mohou být jednotlivá přiřazení kaskádně uspořádána.

| var var2 |
var := 1234 factorial.
var2 
var  50. 

Návratové výrazy

Obdoba return z C/C++ a spol., ovšem místo slova return se použije znak ^. Pokud je návratový výraz uvnitř bloku, je ukončena celá obalující metoda, nikoliv pouze vyhodnocovaný blok. Pokud návratový výraz neuvedeme, vrací se implicitně self.

Bloky

Bloky jsou velmi významnou součástí jazyka Smalltalk. Jedná se o instance třídy BlockContext a vytvářejí se pomocí hranatých závorek [ ] ohraničujících sekvenci výrazů, přičemž výsledkem bloku je výsledek posledního výrazu. Například [ 1. 2. 3 ] je blok, jehož vyhodnocením získáme hodnotu 3.

Bloky mohou přijímat parametry. Vyhodnocení bloku je provedeno voláním metody #value případně #value: pro vyhodnocení bloku s jedním parametrem (#value:value: pro dva parametry atd.). Pro počet parametrů větší než čtyři se používá metoda #valueWithArgu­ments: zpracovávající pole parametrů. V bloku mohou být deklarovány lokální proměnné (za deklarací parametrů).

Parametry bloku jsou uváděny vždy na jeho začátku. Blok s parametry vypadá takhle: [ :param1 :param2 | param1 + param2 ]. Použití tohoto bloku je pak následující (vrátí výsledek 7):  [ :param1 :param2 | param1 + param2 ] value: 3 value: 4.

Bloky jsou samozřejmě objekty, lze je proto použít na pravé straně přiřazení. Jsou to vlastně anonymní funkce.

| aBlock |
aBlock := [ :param1 :param2 | param1 + param2 ].
^ aBlock value: 3 value: 4.

Volání primitivních metod

K explicitnímu volání primitivních metod se běžný programátor dostane jen výjimečně. Zapisuje se následovně: <primitive: 41>. Číslo za dvojtečkou je unikátní číslo primitivy. Pokud volání primitivy selže, může za ní být uveden náhradní kód. Ve speciálních případech se ještě používají <cdecl: >, <apicall: > či jiná podobná volání služeb VM, ale ta souvisí se speciálními pluginy virtuálního stroje pro platformně závislé operace.

A co dál?

Dál už nic. Ze syntaktického hlediska jsme možnosti Smalltalku vyčerpali. Na první pohled to tak určitě nevypadá, ale již máme k dispozici všechno, co potřebujeme k vytváření tříd, metod, řídících konstrukcí, vícevláknových aplikací, strukturovanému zpracování výjimek, typové kontrole, provázané dokumentaci, zkrátka ke všemu, co se od moderního programovacího jazyka vyžaduje, ale o tom až v dalších dílech.

Abych nevypadal, že si moc vymýšlím, dopředu vám ukáži, jak vypadá například obdoba C++ cyklu for (int i = 1; i <= 10; i++) { cout << i << endl; };

1 to: 10 do: [ :i | Transcript show: i; cr ].

objektu 1 je poslána zpráva #to:do: se dvěma argumenty, kde prvním argumentem je ukončovací hodnota cyklu a druhým argumentem je jednoparametrový blok. Objekt 1 tento blok desetkrát vyhodnotí s indexem jako parametrem bloku. Blok samotný si tento parametr přečte (na jeho pojmenování nezáleží) a použije ho jako argument zprávy #show: zaslané Transcriptu, což je jistá obdoba standardního výstupu. Ten dané číslo vypíše a na pokyn zprávy #cr odřádkuje.

Tento řádek můžete ve Squeaku takřka kamkoliv napsat, označit a dát pokyn k jeho provedení. To, že jsme nepotřebovali žádné #include, funkci main(), return, spuštění kompilátoru a linkeru, nemá cenu rozvádět a ani by to nebylo vůči kompilovaným jazykům korektní.

Odkazy

www.squeak.org
www.comtalk.net

Našli jste v článku chybu?

26. 2. 2004 9:24

Frantisek Rysanek (neregistrovaný)

Vytecne, tohle je presne to, co mi schazelo.

Dekuji, tesim se na dalsi pokracovani :)



25. 2. 2004 8:57

Frantisek Rysanek (neregistrovaný)

> Ve Smalltalku existují pouze reference na objekty.
> V proměnné nikdy nemůže být objekt jako takový,
> vždy je v ní pouze reference.
> Tedy všechny parametry jsou předávány odkazem.
>
takže při přiřazení se dělá něco jako hard link?

Tomu úplně nerozumím. Jsou situace, kdy chci přiřadit z proměnné do proměnné jenom referenci na instanci nějaké třídy (shallow copy). Pak jsou situace, kdy chci vytvořit novou instanci podle stávajícího mustru (deep copy).
Takže pokud…






Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

DigiZone.cz: „Black Friday 2016“: závěrečné zhodnocení

„Black Friday 2016“: závěrečné zhodnocení

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase

Podnikatel.cz: 1. den EET? Problémy s pokladnami

1. den EET? Problémy s pokladnami

DigiZone.cz: Flix TV startuje i na Slovensku

Flix TV startuje i na Slovensku

Vitalia.cz: Znáte „černý detox“? Ani to nezkoušejte

Znáte „černý detox“? Ani to nezkoušejte

DigiZone.cz: Recenze Westworld: zavraždit a...

Recenze Westworld: zavraždit a...

Vitalia.cz: Spor o mortadelu: podle Lidlu falšovaná nebyla

Spor o mortadelu: podle Lidlu falšovaná nebyla

Vitalia.cz: Co pomáhá dítěti při zácpě?

Co pomáhá dítěti při zácpě?

Podnikatel.cz: Udávání kvůli EET začalo

Udávání kvůli EET začalo

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech

Vitalia.cz: I církev dnes vyrábí potraviny

I církev dnes vyrábí potraviny

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

120na80.cz: Na ucho teplý, nebo studený obklad?

Na ucho teplý, nebo studený obklad?

Podnikatel.cz: K EET. Štamgast už peníze na stole nenechá

K EET. Štamgast už peníze na stole nenechá