Resty z minula ============== ## Literály pro signatury a captures :(Int, Str) <-- literál typu signatura, lze jím smart-matchovat \(1, 2, :quack) <-- literál typu capture ## Návratové typy funkcí sub f ($x --> Int) { ... } <-- Nil a Failure vyhovují vždy sub g ($x) returns Int { ... } <-- totéž sub h (0 --> 0) { } <-- fixní výsledek pro fixní argument ## Type captures sub f(::T $x, T $y) { <-- $x a $y musí být stejného typu my T $z = ...; <-- "T" se chová jako typová proměnná } ## Feed operators @list ==> map {.uc} ==> sort ==> my @result; <-- zleva doprava # Na pravé straně buď proměnná, nebo funkce, které se levá # strana předá jako poslední argument. @result = (@list ==> map {.uc} ==> sort); # Je to výraz, jde přiřadit, ale pozor na priority. @result <== sort <== my @unsorted = map &uc <== @list; <-- nebo pozpátku @result <== sort <== my @unsorted <== map &uc <== @list; # Kdekoli si můžeme ^ uložit mezivýsledek. ## Další operátory $condition ?? $if-true !! $if-false <-- jako ? : v Céčku "nebudu zlobit\n" x 100 <-- opakování stringu xx 100 <-- opakování seznamu rand xx 100 <-- levá strana se počítá opakovaně 0 xx * <-- nekonečná posloupnost Objektově orientované programování ================================== Třídy a objekty --------------- class Animal { <-- definice třídy (typu) has $!num-legs; <-- atribut method domesticate() { ... } <-- metoda } ## Atributy has $!num-legs; <-- soukromý atribut (přístupný v metodách) has Int $!num-tails; <-- může být typovaný has $.name; <-- navíc vygeneruje veřejný accessor: metodu pro čtení atributu has $.name is rw; <-- metoda vrací kontejner, takže lze psát $dog.name = "Fido"; ekvivalentní: has $!name; method name is rw { $!name } $.name nebo $!name <-- čtení atributu zevnitř třídy $!name = ... <-- zápis (pokud je accessor R/O, nemůžeme $.name = ...) has $.age is required; <-- nutno vyplnit při vytvoření objektu has $.since-age = self.age; <-- default (vyhodnotí se při vytvoření objektu) my $num-cats; <-- pozor, toto je proměnná třídy ## Metody method meow($loudness) {...} <-- "self" uvnitř odkazuje na aktuální objekt method bark($dog: $shyness) {...} <-- vlastní pojmenování invokantu ($dog === self) method defend($_:) { .bark; .bite; } <-- invokant (self) je téma, užitečná zkratka $.meow(11) <-- volání metody na sobě (jako self.meow(11)) atributová syntaxe $.name je jen speciální případ method !sleep() {...} <-- privátní metoda, volá se self!sleep() multi method ... <-- metoda s více definicemi (jako multi sub) multi method bark(Dog $ where {.age>10}:) {} <-- multi-dispatch podmínka na invokanta multi method bark() <--> multi method bark (Dog $:) method FALLBACK($name, *@a) <-- ošetření nenalezené metody ## Dědičnost (jednoduchá) class Dog is Animal {...} <-- odvozujeme z třídy Animal submethod howl() {...} <-- submetody se nedědí callsame, callwith, nextsame, nextwith; <-- chodí po předcích (v pořadí MRO, viz níž) Dědičnost doopravdy ------------------- class Cat is Animal is Devil <-- specifikujeme více předků class Cat { also is Animal; also is Devil; } <-- alternativně ## Spočítá se pořadí tříd, v jakém se mají hledat metody: $obj.^mro <-- lze se zeptat, jak vypadá $obj.^methods <-- jaké metody jsou k dispozici ## Co od pořadí chceme: - lokálně: levý předek má přednost před pravým - toto má platit i ve všech předcích (nemusí jít splnit) - monotonie: pokud umím nějakou metodu najít a nepochází ode mě, umí ji najít i některý z přímých předků ## Používáme algoritmus C3 (původně pochází z jazyka Dylan) - rekurzivně spočítáme linearizace přímych předků - sléváme tyto linearizace + naše uspořádání přímých předků: - vždy odebereme _první_ začátek seznamu, který se nevyskytuje uvnitř žádného jiného seznamu, a přesuneme ho na konec výstupu - pokud žádný takový neexistuje, ohlásíme chybu ## Multimetody a dědičnost Potomci mohou nahrazovat existující multi varianty, přidávat podrobnější nebo nekompatibilní: class C { multi method m(Str) { "str" } multi method m(Numeric) { "numer" } } class D is C { multi method m(Str) { "new str" } # nahrazení varianty multi method m(Int) { "int" } # přidání přesnější multi method m(@) { "array" } # přidání nekompatibilní multi method m($, $) { "two" } # též } D.m("hello") --> "new str" D.m(42) --> "int" D.m(3.14) --> "numer" (zděděná obecnější varianta) D.m([1,2,3]) --> "array" D.m(1,2) --> "two" Ve skutečnosti je dispatch zděděných metod obyčejným multi-dispatchem, který navíc uvažuje invokanta jako nultý argument. Kód výše je ekvivalentní: class C { } class D is C { multi method m(C: Str) { "str" } multi method m(D: Str) { "new str" } # přesnější v nultém argumentu multi method m(C: Numeric) { "numer" } multi method m(D: Int) { "int" } # přesnější v obou argumentech multi method m(D: @) { "array" } # jediná vyhovující multi method m(D: $, $) { "two" } # jediná vyhovující } Nelze ale přidat obecnější variantu, protože pořadí dle MRO (resp. dle nultého argumentu) bude v konfliktu s pořadím dle prvního argumentu: class C { multi method m(Int) { "int" } } class D is C { multi method m(Numeric) { "numer" } } D.m(3.5) --> "numer" D.m(42) --> ERROR # Ambiguous call to 'm'; these signatures all match: # :(C $: Int $, *%_) # :(D $: Numeric $, *%_)