Tutoriel Programmation objet immersive en Pharo proposé par l'inria disponible à l'adresse https://mooc.pharo.org et sur la plateforme FUN-MOOC.
Présentation par Stéphane Ducasse, directeur de recherche, à l'Inria.
Cette séance va être consacrée à un survol de la syntaxe. Donc, on va vous montrer l'entièreté de la syntaxe. Ne pas s'inquiéter si tout ne vous apparaît pas clairement. On reprendra tous ces éléments plus tard. Par contre, l'idée, c'était de vous montrer l'ensemble de ce qu'il y a dans Pharo.
On va voir les éléments syntaxiques principaux. On va voir qu’il y a trois sortes de messages. Et puis, on va voir la syntaxe sur les blocks qui est un élément essentiel de Pharo. Chacun de ces points seront repris dans des séquences ultérieures.
Juste pour le rappel, vous avez toute la syntaxe sur ce transparent. Là, par exemple, maintenant je peux venir un petit peu dans les détails, vous avez une définition de méthode. Là, je définis la méthode exampleWithNumber
. Elle a un argument.
Par exemple, je vais avoir des définitions locales de variables avec les barres verticales |var|
.
J'ai le Return représenté par le caractère ^
qui me permet de rendre une valeur.
Je vais avoir des boucles, une affectation avec :=
.
Le point, par exemple ça va être un séparateur d'instruction.
Commençons par le traditionnel Hello World.
'Hello World' asMorph openInWindow
D'ailleurs, toutes les expressions, vous pouvez les exécuter dans Pharo vous-même pour voir ce que donne le résultat. Là, j'ai une chaîne que je convertis dans une Morph (c'est un élément graphique) et je demande à cet élément graphique de s'ouvrir dans une fenêtre. J'ai envoyé le message asMorph à l'objet String et j'ai envoyé le message openInWindow à l'objet Morph résultant. Donc là, on a eu deux envois de messages et un objet String.
Un exemple plus compliqué, imaginons que vous vouliez prendre le logo Pharo qui se trouve sur le web, on va faire ça de la manière suivante:
(ZnEasy getPng: 'http://pharo.org/web/files/pharo.png') asMorph openInWindow
On va écrire ZnEasy getPng avec une chaîne qui représente l'url de la ressource qu'on veut lire. Et encore une fois, on va la convertir en chaîne, en un objet graphique et lui demander de s'ouvrir dans une fenêtre. Là, qu'est-ce qu'on voit ? On voit que ZnEasy est une classe parce que les classes commencent par une majuscule. Le message getPng: les deux points, ça veut dire que je j'attends un argument ici la chaîne'http://pharo. org', j'ai cette chaîne-là qui est l'argument de la méthode getPng. Donc, les messages as Morph et openInWindow sont lus de gauche à droite. Encore une fois, vous pouvez vraiment copier-coller ce code, ou le retaper dans Pharo, l'exécuter et vous allez récupérer le logo Pharo.
Commentaire | “Un commentaire s'écrit entre guillemets” |
---|---|
Caractère | $c $# $@ |
Chaîne de caractères | 'un message' |
Symbole | #mac #+ |
Tableau | #(12 32 10) |
Entiers | 12 2r1100 |
Réels | 1.3 6.03e-34 2.4e7 |
Booléens | true false |
undefined | nil |
point | 10@120 |
Si on regarde ce qu'il y a comme élément syntaxique dans Pharo, vous avez les commentaires qui commencent avec des doubles quotes. Ça, c'est un commentaire.
Des caractères, les caractères sont toujours avec dollar quelque chose, $A, c'est le caractère A. Vous avez des chaînes, les chaînes commencent et finissent par une simple quote.
Vous avez des symboles, les symboles ce sont des chaînes uniques. Souvent, ça commence comme ça, un symbole avec le symbole dièse, donc #A c'est le symbole A. Vous avez aussi des tableaux de littéraux.
Là, j'ai un tableau avec 12, 32 et 10. J'ai un tableau à trois éléments.
Après, vous avez les entiers, ils ont plusieurs représentations, mais dans ce cours, on ne va pas s'appesantir dessus. En général, on va utiliser des représentations à base 10, mais si vous voulez une représentation à base 2, vous pouvez aussi.
Vous avez des réels, c'est pareil. Si vous en avez besoin, vous trouverez la syntaxe.
Vous avez les booléens avec True ou False.
Vous avez la valeur indéfinie, le nul de Java qui est le nil en Pharo qui est instance de la classe UndefinedObject.
Et vous avez les points, un point, ça se décrit en Pharo sous la forme de 10@200, par exemple. Ça veut dire, je crée le point 10 qui a X en 10 et 200 en Y. Maintenant, on a vu à peu près les éléments essentiels pour l'aspect syntaxique.
Vous avez les constructeurs du langage. Vous avez définition de variable, définition de variable temporaire, je vais mettre barre verticale suivie d'une barre verticale.
L'affectation, c'est deux points égal :=
, ça c'est l'affectation, je mets dans la variable var la valeur Value. J'ai des messages dans mon système. Comment je les sépare ? Je les sépare par des points quand je veux avoir une séquence de message.
J'ai le retour, ça veut dire, une fonction ou une méthode va rendre une valeur, je vais utiliser le “chapeau” qu'on appelle “caret” en anglais ^
. Après, vous avez des blocks qui sont, de manière technique, des cultures lexicales et qu'en première instance, vous pouvez prendre comme des méthodes anonymes. Je vous montre ce qu'est un block. Un block, ça se définit avec des crochets et ça s' exécute en utilisant le message value
, on va voir ça aussi. Vous avez tous les constructeurs du langage.
Si on regarde, qu'est-ce qu'un programme en Pharo, son essence? C'est créer des objets, qu'on a sûrement créés en utilisant des messages, envoyer des messages à ses objets et, de temps en temps, utiliser des blocks ou des méthodes anonymes. C'est tout.
Maintenant, il y a trois types de messages. Pourquoi y a-t-il trois types de messages dans Pharo? C'est principalement pour minimiser le nombre de parenthèse qu'on utilise. Vous allez voir, c'est relativement simple.
Vous avez les messages unaires, un message unaire c'est toujours un receveur, un sélecteur. Là, par exemple, j'ai dit
"Exemples de messages unaires" 9 squared Date today
je veux 9². Je veux obtenir la date d'aujourd'hui, j'envoie le message today à l'objet date, à la classe date. Il n'y a pas d'arguments, ce sont des messages unaires.
Vous avez les messages binaires. Les messages binaires sont toujours de la forme un receveur, un sélecteur, un argument. Mais le sélecteur, ça sera +, -, ~, ~, =, =, /, /, /, ce genre de choses. Tout ce qui a l'air de grigri mathématique, c'est pour les messages binaires. Là, on voit par exemple 1 + 2, c'est un message. On envoie le message plus à l'objet 1 avec comme argument l'objet 2. @ pour la création de points, c'est aussi un message. C'est un message binaire. Donc là, je crée le point 3 4 en envoyant le message @.
Après, vous avez des messages qu'on appelle des messages à mots-clés. Un message à mots-clés, c'est vraiment de la forme receveur, une clé, un argument, une clé, un argument. L'exemple que vous avez, c'est 2. Est-ce que 2 est entre 10 et 20 ? Si vous l'écriviez dans une syntaxe Java ou C, vous l'écririez de la forme suivante:
2.betweenAnd(10,20)
Dans Pharo, on va l'exprimer sous forme clé valeur:
2 between: 10 and: 20
On va revoir ça, évidemment, mais c'est pour vous donner cette idée de la syntaxe.
Quand vous avez des messages, il y a une précédence (ordre de priorité) entre ces messages:
(Msg) > Unaires > Binaires > Mots-clés
Le système va toujours exécuter en priorité les parenthèses, puis les messages unaires, puis les messages binaires et les messages à mots-clés. Pourquoi? C'est l'idée de nous éviter d'écrire trop de parenthèses. On aurait très bien pu avoir un système où on n'a pas tout cette différenciation, mais on mettrait des parenthèses partout et ça rendrait le code illisible.
Je vais revenir sur chacun de ces types de messages de manière très simple. Un message unaire, par exemple:
10 000 factorial
.
Faites-le, vous allez voir, c'est un très gros chiffre. 10 000 factorial, j'envoie le message factorial
à l'objet 10000. Il n'y a pas d'argument. C'est un message unaire. De la même manière, un message binaire. 1 + 3, c'est un message binaire. Le sélecteur, ça veut dire le nom de la méthode, là, c'est le caractère plus +
.
Et j'ai envoyé le message plus à l'objet 1 avec l'objet 3 en argument. Un message à mots-clés, c'est ce que je vous expliquais tout à l'heure. Je ne fais juste que répéter parce qu'enseigner, c'est répéter. J'ai un message mots-clés dans lequel j'ai keyword, keyword qui est le message avec les arguments. Vous avez vraiment les arguments qui flottent à l'intérieur du message. L'idée de cette syntaxe, c'était de faire une syntaxe que les enfants puissent utiliser. L'idée, c'est d'avoir quelque chose qui s'apparente à de l'anglais.
On va pouvoir introduire les arguments à l'intérieur de nos messages. Regardez un exemple. Imaginons que je veuille faire une requête http.
Je crée une instance de ZnClient avec le message unaire new, il n'y a pas d'argument, de toute façon il n'y a pas de caractère :
. url:
est un message à mots-clés, il y a un deux points. QueryAt:
put:
est un autre message à mots-clés. Et get est finalement un message unaire.
Ce qu'on voit ici, sur cet exemple, ce sont les caractères ;
, on appelle ça une cascade. La cascade, c'est l'idée de dire je veux envoyer tous ces messages au même objet. Ça évite d'avoir une variable temporaire pour rien et ça permet d'écrire du code un petit peu plus compact.
Ce qu'il faut voir, c'est que les messages sont absolument partout dans Pharo. Les conditions, les boucles, les itérateurs, la concurrence, tout est exprimé sous forme de message. Ce que je vous ai expliqué sur les trois messages précédents va s'appliquer partout. C'est la pierre angulaire du système. Il n'y a rien d'autre que ces messages-là.
Regardons. Là, j'ai pris la définition débile de factoriel. Comment est-ce que j'ai défini ce factoriel? Je définis ma méthode factorielle, je mets un commentaire pour la méthode parce qu'il y a des doubles quotes et je commence à dire, self, si l'objet qui reçoit le message est 0, alors je vais rendre 1, si self est positif, par exemple, si je fais factoriel 6, self vaudra 6. Si c'est vrai, à ce moment-là que vais-je faire, je vais rendre quoi, 6 multiplié par 6 moins 1 factoriel, la définition récursive qu'on connaît tous. Ce qui est intéressant de regarder, c'est de voir ici que le message ifTrue qui est une condition, c'est aussi un message parce que c'est un message à mots-clés, il a deux points qui est envoyé à un booléen. Donc vous avez aussi ifTrue, ifFalse, ifFalse, ifFalse, ifFalse, toutes ces conditions sont des messages qui satisfont les règles et qui sont de la sorte des messages à mots-clés que j'ai présentés précédemment. Donc, les conditions sont des messages.
Les boucles sont aussi des messages, ci dessous un exemple:
1 to: 4 do: [ :x | x printOn: Transcript base:2. Transcript cr ]. Transcript flush; cr.
Ici on fait une boucle de 1 à 4. Chaque occurence éxécute le block (instructions entre [ ]
) , un block c'est une méthode anonyme.
Je vais lui dire je veux que tu affiches la valeur de x, et x va prendre successivement les valeurs 1, 2, 3 et 4. Là, ce que vous voyez c'est que to: do:
est un message à mots-clés, c'est un message qui va représenter une boucle.
Il y a plein d'autres boucles dans Pharo, timesRepeat: to:by:do:
etc. On va les voir dans le cours. Ici, le message to:do:
est envoyé à l'objet 1 (entier SmallInteger) avec comme paramètre 4 et un block. On va voir ce qu'est un block rapidement après.
Vous avez aussi des itérateurs. Voici un exemple:
#(12 -4 -86) do:[ :each | Transcript show: each abs printString ; cr] " Le même code peut se présenter aussi" #(12 -4 -86) do:[ :each | Transcript show: each abs printString ; cr]
Les itérateurs, c'est différent, c'est que j'envoie le message, par exemple, do: à la collection en lui disant ce que je veux appliquer sur chacun des éléments. Là, le each
, l'argument de ma fonction va prendre comme valeur 12, -4 et -86. Ce que je fais, c'est que le each, je vais lui appliquer la valeur absolue et je vais l'afficher. Encore une fois, ça sera un autre cours, on vous expliquera les itérateurs. Les itérateurs sont des choses qui arrivent en Java 8 et ça fait partie du noyau de Pharo.
Qu'est-ce qu'un block ? Un block, c'est comme une fonction, ça veut dire que si en maths vous écriviez:
fct(x) = x² + 3
Ce traitement peut être traduit en Pharo par la syntaxe suivante:
" Définition de fct" fct := [:x | x * x + 3 ] " Pour calculer l'image de 2 par la fonction fct, fct(2)" fct value:2
Un block commence par des crochets. Là, le block a un argument qui est x et je vais mettre un core qui est plus 3, x + 3. Et maintenant, ce block quand je veux l'exécuter, l'application de fonction par rapport à la définition, là c'était la définition de fonction, maintenant j'ai l'application de fonction. Je vais envoyer le message Value au block.
Là, si je fais value 2, x vaudra 2 et mon block va me rendre 5. Qu'est-ce qu'un block ? On peut considérer que c'est une fonction anonyme, ça n'a pas de nom, mais dans la réalité, c'est une clôture lexicale.
On vous expliquera un peu ce que ça veut dire. Sinon, les blocks sont des vrais objets à part entière. Je peux les passer comme argument de méthode, je peux les stocker dans des variables d'instance, ils peuvent être retournés et ça nous sert, par exemple, pour les triggers de boutons, pour des callback dans des applications web, ils sont faits avec des blocks. L'utilisation des blocks, on l'a déjà vue, par exemple pour les itérateurs, vous avez eu des blocks. On les a eus aussi pour les conditions, on a utilisé des blocks dedans. Il y aura un cours dédié au block.
C'est quelque chose de très élégant, ça a été introduit dans Eiffel, je l'ai dit récemment, ça a été introduit dans Java 8. Il y a eu une première version qui a été aussi introduite dans C Sharp, c'est quelque chose qui est fondamental.
Il se trouve que les blocks sont un élément fondamental de la syntaxe et du modèle de calcul de Pharo. Comment est-ce qu'on définit une classe dans Pharo?
Le browser va vous montrer des packages, vous allez avoir un ensemble de classe à l'intérieur, et ils vous montrent un template. Vous avez là un template qui dit, par défaut, tu es ReadObject et puis il faut mettre le nom de la classe. On va voir ça sur la classe point, par exemple. Comment est définie la classe point ? J'ai la classe Object, je lui envoie le message, c'est aussi un message, le message subclass instanceVariableNames, etc. Je dis je veux définir la classe point et je lui donne les variables d'instances X et Y. C'est juste pour vous montrer que la syntaxe suit toujours le même modèle avec des messages. En général, on ne s'amuse pas à l'écrire à la main, l'outil vous donne le template et puis vous remplissez dedans.
Comment est-ce qu'on définit une méthode? Les méthodes sont publiques et virtuelles et que, par défaut, elles retournent self.
Quand on est dans le browser de code, ce qu'on va voir, il nous propose aussi un template. Le template, ça va être le nom de la méthode potentielle. Un commentaire parce qu'on est des gens bien élevés et qu'on commande notre code. Et puis, potentiellement, on va utiliser des variables temporaires et des expressions. Qu'est-ce que ça donne? On a déjà vu une méthode factorielle. Par exemple, je suis dans la classe Integer, je regarde la méthode factorielle. Là, j'ai le code de la méthode factorielle avec le nom de la méthode, son commentaire et le core de la méthode que j'ai définie.
En résumé, qu'avons-nous vu ? On a vu qu'il y avait trois sortes de messages dans Pharo. Il y a les messages unaires où il n'y a pas d'arguments, les messages binaires où les sélecteurs sont tout ce qui est mathématique et les messages à mots-clés qui sont les messages où il y a un ou plusieurs arguments, qu'il y a une priorité, ça veut dire qu'on va exécuter les parenthèses en premier, puis les messages unaires, binaires et à mots-clés; et que si on est au même niveau, on va exécuter de gauche à droite. Et on a vu toute la syntaxe.
Cette séance est terminée. Qu'avons-nous vu? La syntaxe est très, très compacte. Il y a très peu de constructeurs et ils sont tous très expressifs. Ce sont principalement des messages et des fermetures lexicales ou des blocks. Il y a trois types de message. On vous fera faire un petit exercice pour développer un petit langage. Ça permet de définir des langages qu'on appelle Domain Specific Languages.