OOP 2 ===== ## Konstruktory my $a = Animal.new(name => 'Fido', age => 10); <-- metoda zděděná z třídy Mu submethod BUILD() { $!age < 100 or die } <-- inicializátor --> doopravdy se uvnitř new děje toto: - Animal.CREATE() - $obj.BUILDALL(init) - $obj.BUILD(init) všech předků od nejstaršího - kdykoliv chybí, atributy se nastavují veřejnými metodami - pokud se atribut nenastaví, použije se default - $obj.TWEAK(init) všech předků od nejstaršího ## Vlastní konstruktor (můžeme též předefinovat new) method new($name) { self.bless(:$name, $age => 0) # self je v tomto okamžiku typový objekt # bless volá CREATE + metody na nastavení atributů } ## Klonování objektů method clone(attr => $val) <-- zděděna z Mu, vytvoří kopii objektu včetně soukromých atributů method clone { <-- clone můžeme předefinovat my $obj = callsame; $obj.id = rand; $obj } Delegace -------- class Cat { has $.claws handles ; has $.voice handles 'be-happy' => 'purr'; } Pak: $cat.scratch <---> $cat.claws.scratch $cat.be-happy <---> $cat.voice.purr Kompozice --------- role Hunter { method hunt($what) { self.catch($what); self.eat($what); } method catch {...} <-- tyto vyžaduji od třídy method eat {...} } class Cat does Hunter { } <-- do třídy vložím roli # Pokud více rolí specifikuje tutéž metodu, musím kolizi ošetřit # předefinováním metody uvnitř třídy. class Cat does Hunter does Ranger { method hunt($what) { ... } } role BinaryTree[::Type] <-- parametrizovaná role (obecná signatura) $rabbit but Hunter; <-- mixin: run-time kompozice "Mňau" but False; $cat ~~ Cat <-- konkrétní typ $cat ~~ Animal <-- funguje též předek $cat ~~ Hunter <-- nebo role sub exterminate-rats(Hunter $h); <-- lze používat v signaturách Typy a meta-objekty =================== my $x = Int <-- typový objekt, chová se jako undef $x.defined <-- False my Cat $c .= new(:3age); <-- $c je inicializováno typovým objektem, na něm .= zavolá new Mu <-- předek všechny typů (nejobecnější undef) Any <-- předek "normálních" typů (potomek Mu) (u tříd je default "is Any") my Cat $c = Nil; <-- univerzální undef, zde se přetypuje na Cat hodí se třeba jako parametr funkce: sub f(Cat $c) {...}; f(Nil) <---> f(Cat) Na typových/undef objektech lze volat metody. Hodí se třeba jako koncový případ rekurze: class BinTree { has ($.left, $.right); multi method nodes(BinTree:U:) { 0 } multi method nodes(BinTree:D:) { 1 + [+] ($.left, $.right)>>.nodes } } Meta-metody ----------- .VAR <-- vrátí kontejner .WHICH <-- unikátní identifikátor objektu .WHERE <-- adresa v paměti .WHY <-- dokumentace :) .WHAT <-- vrátí typový objekt .HOW <-- "High Order Workings" vrátí meta-objekt spravující typový objekt $x.^methods <-- zkratka za $x.HOW.methods($x) $x.^mro <-- method resolution order $x.^name <-- jméno typu $x.^attributes <-- seznam atributů $x.^parents(:all) <-- předkové (včetně Cool, Any, Mu) Vytváření tříd za běhu ---------------------- # Rakudo 2017.01: nefunguje v interaktivním módu constant Feline := Metamodel::ClassHOW.new_type( name => 'Feline' ); Feline.^add_method('voice', my method voice(Feline:) { say 'mňaeiou!' }); my $a = Attribute.new(name => '$!claws', package => Feline, type => Int, has_accessor => 1, is_rw => 1); Feline.^add_attribute($a); Feline.^compose; <-- pozor na sigily: $feline.^compose se volá na kontejner! my $cat = Feline.new; say $cat.^methods; $a.set_value($cat, 18); say $cat.claws; Standardní typy =============== Prapředci --------- Mu (předek všech typů) defined isa(typ) <-- je tento objekt daného typu? does(typ) <-- dá se použít jako daný typ? (potomek, role, ...) Bool, Str gist --> Str <-- lidsky čitelná podoba (ta, co vypíše say) perl --> Str <-- strojově čitelná podoba item new, clone, bless, CREATE print, put, say ACCEPTS($x) <-- vyhodnocuje $x ~~ self Any (předek všech běžných typů) + všechny běžné seznamové metody jsou definovány zde, protože každý objekt se umí chovat jako jednoprvkový seznam Cool (string nebo číslo podle potřeby) + všechny běžné metody pro čísla i stringy; zkonvertuje operand na specifický typ a metodu na něj redispatchuje Skalární typy ------------- Bool Int / IntStr / UInt <-- s libovolnou přesností Rat / RatStr / FatRat <-- racionální číslo Num / NumStr <-- float Complex / ComplexStr <-- komplexní float Str <-- string (více později) Scalar <-- skalární kontejner role Numeric <-- cokoliv číselného role Real <-- číslo bez imaginární části role Rational[num, den] <-- racionální číslo se zadanými typy čit./jmen. Rat = Rational[Int, UInt64] FatRat = Rational[Int, Int] role Stringy <-- cokoliv stringovitého Seznamovité typy ---------------- Seq <-- obecná posloupnost (lze ji iterovat či učinit seznamem) List <-- seznam (potenciálně nekonečný), lze iterovat i indexovat; [není potomek Seq] do prvků lze přiřazovat, jen jsou-li to kontejnery Array <-- seznam kontejnerů Range <-- generátor intervalu Slip <-- List, který se automaticky zplacaťuje role Positional <-- cokoliv indexovatelného [...] role Iterable <-- cokoliv iterovatelného iterator <-- vratí iterátor flat <-- vrátí zplacatělou verzi Slovníkovité typy ----------------- Map, Hash Set, SetHash Bag, BagHash Mix, MixHash role Associative <-- cokoliv indexovatelného {...} \-- role QuantHash <-- kolekce objektů reprezentovaných jako klíče heše \-- role Setty \-- role Baggy \-- role Mixy Různé ----- Order <-- výsledek <=> Pair <-- výsledek => Whatever <-- * Capture Signature Junction <-- není potomek Any, dědí přímo z Mu Exception / Failure Blob / Buf <-- read-only/read-write posloupnost integerů, umí se konvertovat na binární reprezentaci a zpět podle zadaného předpisu Programovité typy ----------------- Code <-- kus kódu se signaturou (umí introspekci) \-- Block <-- navíc má lexical scope \-- Routine <-- odchytává return, umí multi-dispatch \-- Sub <-- subrutina / operátor / trait \-- Method <-- stará se o self, nestěžuje si na extra argumenty \-- Submethod <-- nedědí se \-- WhateverCode <-- lambda vzniklá z whatever role Callable <-- cokoliv, co se dá zavolat (...) Traity ------ multi sub trait_mod:(Routine $r, :$verbose!) # -> pak můžeme psát: sub funkce is verbose { ... }, trait se spustí při překladu # -> podobně se dají definovat traity metod a kontejnerů { $r.wrap( -> |c { say "Entering " ~ $r->name; my $ret = callsame; say "Leaving " ~ $r->name; $ret }); } Výjimky ======= Třídy odvozené od Exception, název obvykle X::Něco, např. X::IO::DoesNotExist. X::IO::DoesNotExist.new(path=>"file.txt").throw; <-- vyhození výjimky die X::IO::DoesNotExist.new(path=>"file.txt"); <-- taktéž die "Něco se pokazilo"; <---> die X::AdHoc.new(:payload); # Ad-hoc výjimka (pokud nechcete vytvářet speciální třídu). # Payload obvykle stringová hláška, ale může to být libovolný objekt. warn "Pozor schod!"; <-- warning, defaultně se vypíše na stderr a pokračuje, ale volající mohou odchytit quietly { warn ... } <-- potlačí warningy (i v zavolaných funkcích) Zachytávání výjimek ------------------- try { <-- "try" je nepovinné, CATCH může být v lib. bloku slurp "file.txt" do-something or die "Něco se pokazilo" ... CATCH { <-- CATCH blok (phaser), téma je výjimka when (X::IO::DoesNotExist & {.path eq "file.txt"}) { say "The File does not exist!" } when (X::IO) { say "Some other I/O error" say .backtrace; } when /pokazilo/ { <-- Zachycení adhoc výjimky (regex match hlášky) ... } default { .rethrow; } <-- Ostatní výjimky pošleme výš. Nemusí tu být, neošetřené výjimky automaticky propagovány (úspěšný smartmatch označí výjimku za ošetřenou) } } $data = (try slurp "file.txt") // "default" <-- try jako výraz, vrací undef při chybě Resumable exceptions -------------------- ATTEMPT: loop { <-- (odbočka) loop label, dá se na něj odkázat my $r = (1..6).roll; v next, last a redo say "Rolled $r"; die("oops") if $r != 6; say "I win!"; last; CATCH { when /oops/ { my $exc = $_; ASK: loop { say "Something went wrong. Abort, Retry, Ignore?"; given get.lc { when "a" { $exc.rethrow } when "r" { redo ATTEMPT } <-- opakuje vnější smyčku (nový pokus) when "i" { $exc.resume } <-- pokračuje za místem vzniku výjimky (do-something-more...) default { redo ASK } } } } } }