Skip to content

Instantly share code, notes, and snippets.

@cognominal
Last active March 25, 2021 22:38
Show Gist options
  • Select an option

  • Save cognominal/387a169ce262d7c1fb691ff3e58a2bb1 to your computer and use it in GitHub Desktop.

Select an option

Save cognominal/387a169ce262d7c1fb691ff3e58a2bb1 to your computer and use it in GitHub Desktop.
presentation #prez
=begin pod
=head1 Cette présentation
Stéphane cognominal Payrard -- Journées Perl 2016 -- Samedi 25 Juin 2016
Ce talk
=item Objet et sa place dans les types Perl
=item Multiméthodes
=item Métamodèle OO
=head2
Ce document est dérivé de la présentation censément sur l'objet aux
journées Perl 2016. J'y rajoute sous les =head2 de pod du matériel
que j'y ai fait oralement mais aussi du matériel trop avancé
concernant le compilateur. Il découle d'une démarche commencée en
2012 qui consiste à explorer l'architecture de rakudo en expérimentant
avec le REPL. Dans ce document écrit je ne gommerai pas les
difficultés que j'ai omise à l'oral. Lors de la présentation orale,
j'ai évité les "forward references", elles sont moins gếnantes à
l'écrit. Expérimenter pour apprendre Raku a aussi pour conséquence
d'explorer des zones peu connues et de découvrir des bogues qui sont
généralement corrigés rapidement par la core team. Il n'y a donc pas
de honte à n'être, comme moi, que la mouche du coche.
=head1 Si Raku installé, utiliser le REPL
=item Pas de perte de la portée lexicale
=item Chaque ligne fait un EVAL
=item ... dans une portee emboitée
$ Raku-m # avec le backend MoarVM
To exit type ' exit' or '^D'
> say 'Salut les mongueurs'
Salut les mongueurs
> 'Salut les mongueurs'
Salut les mongueurs
> my $a = 'salut'
salut
> $a
salut
> { my $a }
> $a # le contexte lexical n'est plus accessible
Variable '$a' is not declared
> $a # le mode de Raku est strict par défaut. Déclaration lexicale nécessaire
Variable '$a' is not declared
> no strict; $a
> (Any)
> $a
> (Any)
> class A {}
(A)
> class A {}
Redeclaration of symbol A
=head2 Notes sur le REPL
Les lignes d'une session REPL sont montrées consécutivement
Le REPL est un concept hérité de LISP et permet d'utiliser
interactivement le langage. C'est l'acronyme de Read Eval Print Loop.
En clair. chaque ligne est lue, évaluée par EVAL, la valeur est
automatiquement imprimée et ce ad nauseamn. Chaque ligne est
interprétée dans un contexte lexical nouveau imbriqué dans le
précédent de sorte qu'une variable lexicale déclarée précédemment sans
contexte lexical explicite est visible. Lors d'expérimentation à la
ligne de commande, c'est une nuisance de devoir déclarer les
variables. Heureusement, la portée du pragma C<no script> est
lexicale donc permet de ne pas avoir à déclarer les variables.
Redéclarer des symbole, comme une classe, dans une portée de paquetage est toujours une erreur.
Mais pour supporter cette fonctionnilité, il faudrait un moyen de savoir d'où vient une variable
quand elle peut venir de différentes "versions" d'une portée paquetage donnée.
=head1 Expérimenter avec Raku sans l'installer
Utiliser le bot p6eval
=item grâce à IRC
=item aller sur irc.freenode.net
=item /join #Raku
=item m: say "hello world" # en public
=item /msg camelia m: say "hello world" # en privé
=item sur irc.perl.org #perlfr le bot s'appelle p6eval
=item affichage explicite contrairement au REPL
=head2 Limite du bot
On ne peut avoir de "session" contrairement au REPL puisqu'à chaque ligne
r>akudo est relancé.
=head1 Présentation suivante : les roles
=item roles comme interface matérialisé par les sigils
=item roles paramétriques, un peu comme generics de Java
=head2 d'après jnthn
J'ai utilisé les slides de jnthn.
voir L<Raku roles in depth|http://jnthn.net/papers/2009-yapc-eu-roles-slides.pdf>.
On peut voir ce document comme un complément au cours de jnthn sur les internals de
rakudo et nqp
[L<1|http://edumentab.github.io/rakudo-and-nqp-internals-course/slides-day1.pdf>]
[L<2|http://edumentab.github.io/rakudo-and-nqp-internals-course/slides-day2.pdf>]
Les ops de AST sont documentés
[L<ici|https://github.com/Raku/nqp/blob/master/docs/ops.markdown>] et
l'implémentation des ops de MoarVM est
L<ici|https://github.com/MoarVM/MoarVM/blob/master/src/core/interp.c>.
L'AST étant par définition Abstract, il n'y a pas toujours de
correspondance exacte entre les ops de l'AST et ceux de MoarVM. Les
ops spécifiques à Raku sont implémentés
C<ici|https://github.com/rakudo/rakudo/blob/nom/src/vm/moar/ops/Raku_ops.c>.
=head1 Distinction entre rôle et classe
=item Une classe permet d'instancier des objets
=item On peut composer des classes à partir de roles
=item Classes mutables, roles, pas
=item Mais on peut mixer des roles dans un classe/objet
=item statiquement : trait dynamiquement : mixin
class A {}; class A is B {}
implémenté en terme de :
multi sub trait_mod:<is>(Mu:U $child, Mu:U $parent) { ... }
=head2
Les traits et les mixins sont implémentés en term de multiméthodes C<trait_mode>.
C<< :<is< >> dé
=head1 Programmation Orientée Objet en Raku
=item OO à base de classes versus prototpypes
=item Raku à base de classes
=item Mais métamodèle permet d'implémenter OO à base de prototypes
=item Multiméthodes
=item Rôles (prochaine présentation)
=item Tous les types de Raku ne sont pas objets
=item process multifils et intérêt de l'immutabilité
=head2 OO
Il y a deux genres de programmation objet selon le modèle utilisé pour
crééer des objets. On peut les crééer à partir d'objet prototypes ou
en les instanciant avec l'aide de la classe appropriée. La plupart des
langages OO populaires sont à base de classes, comme Java ou C++, mais
Javascript à base de prototypes.
Une méthode est une routine dont le premier paramètre est l´invocant.
Les routines ont une signature qui fait partie de son nom long.
Plusieurs routine de meme non court sont distinguées par leur signature
Généralement on appelle une routine avec son nom court, le dispatch se
fait avec la routine de signature la plus spécifique.
Tous les types de Raku ne sont pas objets.
Je ne parlerais pas de concurrence mais elle a une influence sur la nature
des données. Pour que des fils (threads) puissent travailler sur
les memes données sans soucis de coordination, il faut qu´elles soient immutables
=head1 Attributs d´objet et accesseur
class A {
has int $!val = 42;
has $.b is rw;
}
twigil ! pour attribut privé non accessible classes dérivées
twigil . pour attribut public
is rw pour attribut en lecture écriture
my A $a = ...
say $a.b # accesseur en lecture <=> appel de méthode
$a.b = 666 # acesseur en écriture
=head1 Méthodes
class Salut {
has $.qui is required;
method salut { say "salut $!qui" }
}
$_ = Salut.new(:qui<dude>); # .new utilise argument nommés
$_.salut;
.salut; # invocant implicite
=head1 Syntaxe pour une classe par fichier
unit class Salut;
has $.qui
method salut() { say "salut $!qui" }my
=head1 Invocant et pseudo-classe
=item invocant par défaut est self
=item pamètre invocant généralement omis de la sigature
=item si explictite, le : separe l'invocant des autres parametres
=item ::?CLASS permet de capturer la classe
=item utile pour classe anonyme
=item ou pour éviter d'utiliser le nom
class A {
method bar { self.doit }
method doit($self:) { ... }
method foo(::?CLASS $self) {
my ::?CLASS $a
}
}
=head1 Instanciation de classes
class Salut {
has $.qui
method salut($quoi) { say "$quoi $!qui" }
}
$_ = Salut.new(:qui<dude>);
.salut: bonjour;
.salut(´bonjour´)
salut $_:
=head1 Attributs et méthodes de classe
Une classe est un module donc déclarateur de scope classique.
=item my : privé
=item our : public
method oh_so_static(::?CLASS:U:) { }
method oh_so_static(A:U:) { }
Explication de signature
=item A nom de classe
=item A:U valeur indéfinie seulement, donc classe pas instance
=item A:U: le paramètre est invocant
=head1 Types et valeurs de défaut des attributs
=item les attributs ont un sigil ! même si déclaré avec .
=item le type implicite d'un attribut est Mu
=item pour accepter les Junctions, dérivé de Mu
=item mais initialisé par défaut à Any
> class A { has $.a; has $!b }; A.^attributes
(Mu $!a Mu $!b)
> say A.new( :a(1|2)).a.WHAT
(Junction)
> A.new.a.WHAT
(Any)
=head1 Accesseur d'attribut rw ou méthode lvalue ?
Même interface pour l'utilisateur
=item l'utilisateur d'une classe n'a pas à connaître ses attributs
=item détail d'implémentation qui peut changer
=item même si attribut publique
=item la méthode .b remplace l'accesseur autogénéré
=item .quelquechose : point comme pseudosigil pour désigner une méthode
method b is rw {
return Proxy.new(
FETCH => method () { say 'fetch'; $!b },
STORE => method ($new) { say 'store' $!b },
);
}
=head1 Containers
=item un Proxy est une forme de container
=item Scalar est le container par défaut
=item .VAR donne le container d'une valeur
=item .WHAT donne le type d'une valeur
=item .VAR et .WHAT sont des macros
> my $a; say $a.VAR.WHAT
(Scalar)
my $a; say $a.VAR # bogue. Décontenairisation parasite
Any
=head2
=head1 Retour sur le container Proxy
#Raku++ timotimo++ pour l'exemple
my $foo := Proxy.new(
FETCH => -> | { "yo" },
STORE => -> | { });
say $foo.VAR.WHAT;
say $foo
(Proxy)
␤yo␤
=head1 Types
int, num, int8... types natifs dont la valeur tient dans un registre
types natifs
objets
=item les types natifs n'ont pas d'annotation de type,
=item les objets, si
=item mais autoboxing: 1.WHAT
=head1 Boxing
=item On peut traiter les types natifs comme des objets
=item Boxing
=item On ne peut "interroger un natif" que par un boxing
=item Transparent pour le programmeur
=item Boxing d'un int donne un Int
=item Boxing se cumule avec les containers
=head1 Exemple de boxing
> my int $i = 42
42
> say $i
42
> $i.say # boxing pour utiliser la méthode .say
42
> $i.WHAT
(Int)
> $i.REPR
P6opaque # classes Raku implémentées en terme de P6Opaque
>
=head1 Type valeur et type référence (1)
Un objet est un type référence.
Il peut etre modifié si passé en argument et si mutable
class A { has $.a is rw };
sub foo(A $val) { $val.a = 42 }
my $a = A.new; # A.new(a => Any )
a($a);
say $a; # A.new(a => 42 )
> my int $foo = 10;
> sub do-it(int $bar is rw) { say $bar.VAR.WHAT };
> do-it($foo)
(IntLexRef)
=head1 Type valeur et type référence (2)
sub foo(int $val) { $val = 42 };
Cannot assign to readonly variable $val
=item $val est passé par référence et peut etre modifié
=head1 Passer par réference
sub foo(int $val is rw) { $val = 42 }; my int $i = 0; foo($i) ; say $i
foo(666)
Expected a modifiable native int argument for '$val'
=head1 Egalité et identité
Opérateurs binaires (arité 2)
=item =:= vrai identité
=item eqv équivalence
=item === identité de valeur
=item == conversion des args à Num avant comparaison
=item eq conversion des args à Str avant comparaison
=head1 Opérateurs sont des exemples de multi subs
=item Noms longs avec des paires adverbiales
multi sub infix:<eq>(\a, \b) { a.Stringy eq b.Stringy }
multi sub infix:<eq>(str $a, str $b) returns Bool:D { ... }
...
multi sub infix:<==>(\a, \b) { a.Numeric == b.Numeric }
=head1 Pseudo sigil backslash
multi sub infix:<eq>(\a, \b) { a.Stringy eq b.Stringy }
=item pseudo sigil \ => pas de containers
> my \a = 1;
1
> say \a;
\(1)
> \a = 2;
\(1)
Cannot modify an immutable Capture
in block <unit> at <unknown file> line 1
=head1 Un exemple de multisub
multi quicksort( [ ] ) { () }
multi quicksort( [$x ] ) { ($x) }
multi quicksort( [$pivot, *@xs ] ) {
quicksort(@xs.grep: * before $pivot),
$pivot,
quicksort(@xs.grep: * !before $pivot),
}
=head1 Quelques exemples
> 'ab' === 'a' ~ 'b'
True
> 'ab' eq 'a' ~ 'b'
True
> 'ab' =:= 'a' ~ 'b'
False
> 'ab' =:= 'ab'
True
>
=head1 Liage dynamique (binding) et affectation
=item Liage : lie un nom
=item Associe un nom à une valeur ou alias une variable.
=item Similaire à un typeglob Perl 5
=item nqp::bind
=item Affectation
=item multisub
> my @a := 1, 2, 3; say @a.WHAT
(List)
> my @a := 1, 2, 3; my @b := @a; my $c := @a; say @a =:= @b =:= $c
True
> @a.VAR =:= @b.VAR =:= $c.VAR
True
> my @a = 1, 2, 3; say @a.WHAT
(Array)
for 1..20 { print $_, Int.new($_) =:= Int.new($_) ?? '' !! '!', ' ' }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15! 16! 17! 18! 19! 20!
=head1 Immutabilité et conteneur
Une valeur immutable est une valeur qu'on ne peux pas modifier
en place; càd sans changer son identité
=head1 Immutabilité : listes, tableaux (1)
Une liste est immutable. Un tableau ne l'est pas.
Liage d'une lise à un Positional.
> my @a := 1, 2, 3 ; @a[0] = 4
Cannot modify an immutable Int
in block <unit> at <unknown file> line 1
> @a.push: 4
Cannot call 'push' on an immutable 'List'
in block <unit> at <unknown file> line 1
> @a.WHAT
(List)
> @a.push: 4
Cannot call 'push' on an immutable 'List'
in block <unit> at <unknown file> line 1
Assignement d'une liste à un Positional
> my @a = 1, 2, 3 ; @a[0] = 42; @a
[42 2 3]
=head1 Immutabilité : listes, tableaux (2)
Mais les conteneurs d'une liste sont mutables
> my $a = 0; my @a = ($a, 1, 2 )
[0 1 2]
> my $a = 0; my @a := ($a, 1, 2 )
(0 1 2)
> @a[0] = 42
42
> @a
(42 1 2)
=head1 Identité de conteneur
VAR($a) =:= VAR($b)
=head1 Déclaration de type
my int $value
int : commence par une minuscule -> non objet
Int : majustcule -> objet
nqp
> my int $a := 1 ; use nqp; say(nqp::isint($a))
1
rakudo
> my int $a := 1 ; use nqp; say(nqp::isint(nqp::decont($a)))
1
=head1 Mu, Any, Junction, Cool, Failure,
=item Mu : classe racine
=item Junction : valeur quantique, parallélisme potentiel
=item Any is Mu : racine de non Junction
=item Cool is Any :
=head1 Junction
> 1|2|3 == 3
False
False
True
> ?(1|2|3 == 3)
True
> say ((1|2|3) == 3).WHAT
(Junction)
>
=head1 .perl, .gist, .Str
=item .Str convertit une valeur en chaîne
=item .gist même chose à usage humain
=item .perl code pour recréer la valeur
> class A {}; my $a = A.new;
A.new
> use MONKEY-SEE-NO-EVAL; EVAL( $a.perl ) eqv $a
True
=head1 .perl, .gist, .Str sur classes et instances
> class A { has $.a }; my $a = A.new
A.new(a => Any)
> A.Str
Use of uninitialized value of type A in string context
Any of .^name, .perl, .gist, or .say can stringify undefined things, if needed.
> $a.Str
A<86630344>
> A.gist, $a.gist
((A) A.new(a => Any))
> A.perl, $a.perl
(A A.new(a => Any))
=head1 .perl, .gist, .Str sur un match
> 'ab' ~~ /(a) $<b>=b /
「ab」
0 => 「a」
b => 「b」
> $/.Str
ab
> $/.gist
「ab」
0 => 「a」
b => 「b」
> $/.perl # ci-dessous, j'ai raccourci la sortie
Match.new(ast => Any, list => (Match.new(ast => Any, list => (), hash => Map.new(())...))
=head1 Métamodèle
=item soubassement du système de classes de Raku
=item + ou - autohébergé
=item macro .HOW pour accéder la métaclasse
> Mu.HOW
Raku::Metamodel::ClassHOW.new
> Mu.HOW.name(Mu)
Mu
> Mu.HOW.^name
Mu
> Mu.HOW.HOW.^name
NQPClassHOW
=head1 Métamodèle
> 42.^parents()
()
> 42.^parents(:all)
((Cool) (Any) (Mu))
> Mu.^methods
(self sink ACCEPTS WHERE WHICH split take return-rw return WHY set_why Bool ... )
> Mu.^can('Str')
(Str)
> Mu.^can('Foobar')
()
> 1.^can('Str')
(Str Str!b
> 1.^can('Str')[0].WHAT
(Method)
> 1.^can('Str')[0].signature
(Mu $: | is raw)
=head1 multiméthodes et grammaire
Avec les classes, le dispatcher choisit la méthode
dont la signature est au plus près des arguments.
La signature la plus précise l'emporte.
Avec les grammaires, on génère un NFA (non deterministic
finite automaton), ce qui signifie que les règles sont
entrées conceptuellement en para
grammar A {
token TOP { <a> }
proto token a { <...> }
token a:sym<a> { 'a' }
token a:sym<aa> { 'aa' }
}
say A.parse('aa');
=head1 Résumé : les concepts clé
=item objet, classe, instance
=item concret, défini, vrai
=item mutable, container
=item sigil, twigil
=item role, itération
=item trait, attribut,
=item identité, égalité
=end pod
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment