Simplicité, dynamicité, réflexivité. Tels sont les maîtres-mots du langage à objets Pharo, digne héritier du fameux Smalltalk. Ces ingrédients constituent un formidable moteur d'innovation. Le résultat est un langage puissant, aux multiples domaines d'application.
En 1980, la sortie de Smalltalk [1] a marqué le tournant de l'informatique vers la programmation par objets. Les idées qu'il a introduites - révolutionnaires à l'époque - ont été largement reprises dans de nombreux langages, tels que Java, Python, Ruby, Scala, Perl et bien d'autres.
Dans cet article, nous présentons Smalltalk à travers le prisme de son digne successeur : le langage Pharo [2]. Diffusé sous licence libre MIT, Pharo a repris depuis plus de 10 ans le flambeau de l'innovation en génie logiciel. Il s'appuie pour cela sur un consortium international, mêlant à la fois des entreprises technologiques pour l'ancrage terrain et des laboratoires de recherche universitaires pour l'innovation [3]. Le tout est piloté par l'équipe RMoD du laboratoire de recherche de l'Inria à Lille.
Une personne avertie en vaut deux
Avant que vous alliez plus loin dans la lecture de cet article, il convient de rappeler l'avertissement suivant :
« Smalltalk est dangereux.C'est une drogue.Je vous conseille de ne pas l'essayer.Il pourrait ruiner votre vie. »Andy Bower, CEO de la société Object Arts Ltd.
Cette boutade souligne l'attachement des Smalltalkiens à leur langage. C'est ce qui fait que la communauté Smalltalk perdure depuis 40 ans. Elle se retrouve à l'occasion de différents événements et notamment d'une conférence internationale, organisée tous les ans par l'association ESUG (European Smalltalk Users Group) [4]. Un cocorico s'impose ici. En effet, ESUG est depuis sa création en 1991 une association française loi 1901.
1. L'histoire de Smalltalk en 3 anecdotes
Dans cette première partie, nous allons survoler l'histoire de Smalltalk. Trois anecdotes permettent d'avoir un aperçu de ce célèbre langage.
1.1 Une origine industrielle, en direct de la Silicon Valley
Quand on parle du langage Smalltalk, c'est en fait de Smalltalk-80 qu'il s'agit. Cette release a été rendue publique en 1980, après une décennie de recherches au sein du PARC (Xerox Palo Alto Research Center). Ce dernier était un laboratoire de Xerox, société connue pour ses photocopieurs.
L'informatique moderne doit beaucoup au PARC. En effet, ce centre de recherches a été à l'origine de multiples technologies. À titre d'exemples, nous pouvons citer : les souris d'ordinateur, les interfaces graphiques fenêtrées, l'impression laser, les réseaux Ethernet, ainsi que l'encre électronique.
1.2 Un nom volontairement humble
Le nom Smalltalk a pour origine l'expression anglaise small talk (en 2 mots), qui peut être traduite en « menus propos » ou « banalités ». Alan Kay, l'un des pères de Smalltalk indique que le choix de ce nom humble a pour but de limiter les attentes, pour mieux surprendre les utilisateurs [5]. Il s'agit, en fait, d'un pied de nez à la tendance de l'époque d'utiliser des noms suggérant beaucoup, pour des langages in fine décevants comme Zeus, Odin et Thor.
1.3 Une montgolfière en guise de mascotte
La mascotte associée à Smalltalk est une montgolfière. Elle trouve son origine dans le numéro hors-série de la revue Byte Magazine dédié à Smalltalk [6]. La montgolfière apparaît pour la première fois sur la couverture de ce numéro-fleuve de 496 pages (voir la figure 1). Elle symbolise la release publique du langage Smalltalk, après une décennie de confinement dans les laboratoires de recherche.
2. Pharo, digne héritier de Smalltalk
Dans cette section, nous présentons les facettes de Pharo héritées de son illustre aïeul. Il s'agit de la quasi-totalité des caractéristiques de Smalltalk, à quelques détails près. C'est ce qui fait que Pharo est un langage à la fois simple à prendre en main, tout en étant très puissant.
La simplicité de Pharo découle d'un ensemble de concepts réduits, une syntaxe minimaliste, ainsi qu'un typage dynamique strict. Par ailleurs, les règles uniformes simplifient davantage l'apprentissage. Quant à la puissance du langage, elle se traduit par une dynamicité hors norme et des capacités réflexives exceptionnelles. Les bibliothèques très riches, ainsi que les outils de développement élaborés participent à la puissance du langage.
2.1 Un ensemble de concepts réduits
Dans Pharo, il y a 2 concepts fondamentaux : les objets et les variables. Les nombres, les booléens, les caractères, les tableaux sont tous des exemples d'objets. Chacun d'eux est associé à une référence unique. Ce sont les références des objets qui sont stockées dans les variables. Pour faire une analogie basée sur le langage C, c'est comme si toutes les variables étaient de type pointeur.
Tout objet est instance d'une classe. Chaque classe définit la structure de ses instances. Cette structure peut être aussi simple qu'un octet dans le cas d'un caractère ASCII. Elle peut être composée de plusieurs variables dans le cas d'objets complexes.
Une classe définit également le comportement de ses instances. Il s'agit des méthodes qui sont exécutées par l'objet en réaction à un appel.
Une partie de la structure et du comportement des instances d'une classe peut être héritée. Comme dans de nombreux langages à objets, l'héritage de Smalltalk est simple. Une classe n'admet donc qu'une unique superclasse. Pharo permet cependant l'héritage multiple, comme nous le présenterons plus loin.
Les règles de visibilité sont simples. Toutes les classes sont publiques et peuvent être référencées et utilisées dans n'importe quelle méthode. Toutes les méthodes sont publiques et peuvent être appelées de n'importe où. En revanche, la structure des objets est privée, au sens le plus strict du terme. Seul l'objet lui-même peut lire ou modifier sa structure.
2.2 Une syntaxe minimaliste
La syntaxe de Pharo est minimaliste. L'illustration de l'ensemble des règles et des caractères spéciaux occupe à peine le côté message d'une carte postale [7] (voir la figure 2).
2.2.1 Un tout petit ensemble de mots réservés
Dans Pharo, il n'y a que 6 mots réservés [8] : 3 constantes et 3 pseudo-variables. Deux constantes représentent les booléens : true et false. La troisième constante est représentée par le mot réservé nil. Il s'agit de l'objet par défaut, référencé par toute variable non initialisée. Contrairement à la référence nulle qu'on trouve dans d'autres langages, nil est un objet en bonne et due forme, instance d'une classe quasi vide.
Notion de pseudo-variable
Comme son nom l'indique, une pseudo-variable est une variable. Sa valeur peut changer. Cependant, cette valeur dépend uniquement du contexte. L'utilisateur ne peut pas la modifier.
L'objet courant est représenté par la pseudo-variable self. Elle référence, au sein d'une méthode, l'objet qui exécute ladite méthode. self peut être utilisée comme paramètre ou pour appeler une méthode sur l'objet courant. Dans ce dernier cas, la recherche de la méthode à exécuter commence dans la classe de l'objet.
La pseudo-variable super référence également l'objet courant. Mais, elle ne peut être utilisée que pour appeler des méthodes. L'utilisation de super fait que la recherche de la méthode à exécuter commence dans la superclasse de la classe qui contient l'appel.
Enfin, le dernier mot réservé est thisContext. Cette dernière pseudo-variable permet d'accéder à la pile d'exécution et de la modifier. C'est une manifestation du caractère réflexif de Pharo.
2.2.2 Des noms de méthodes favorisant la lisibilité
Une facette qui reste unique dans la syntaxe Pharo est le nommage des méthodes. Une méthode qui ne prend pas de paramètres est dite unaire. Elle a un nom simple. C'est le cas des méthodes de trigonométrie telles que cos et sin. Notez l'absence de parenthèses. Ainsi, le cosinus de 0.78 radian peut être obtenu à l'aide de l'expression suivante :
Il est possible de chaîner les appels des méthodes pour que l'une soit appelée sur le résultat de l'autre. Prenons l'exemple du système de fichiers. On peut obtenir le dossier de travail de Pharo en appelant la méthode unaire workingDirectory sur la classe FileSystem. Le résultat est une référence sur un fichier (instance de la classe FileReference). Par ailleurs, le chemin (instance de la classe Path) pour accéder à un fichier peut être obtenu à l'aide d'une autre méthode unaire path. Ainsi, l'expression suivante donne le chemin vers le dossier de travail de Pharo :
Le nom d'une méthode peut être composé de caractères non alphanumériques. Une telle méthode est dite binaire et prend un seul paramètre. C'est le cas des opérations arithmétiques ou des comparaisons qui sont définies dans les classes des nombres. Cela dit, n'importe quelle classe peut avoir de telles méthodes. Dans l'exemple ci-dessous, nous construisons l'objet qui représente le dossier usr. La méthode / est appelée sur l'objet qui représente la racine du disque, résultat de la méthode unaire root. Le paramètre est la chaîne de caractères usr.
Les appels de méthodes binaires peuvent aussi être chaînés. Ils sont alors évalués de gauche à droite. Chaque méthode est appelée sur le résultat de la précédente. L'expression suivante illustre ce chaînage. Nous pouvons exprimer de façon très naturelle l'objet qui représente le fichier /usr/bin/yes.
Le troisième et dernier cas est celui des méthodes dites à mots-clés. Ce sont des méthodes destinées à prendre un ou plusieurs paramètres, et dont le nom est constitué de caractères alphanumériques. Ce nom est composé de plusieurs mots. Chaque mot est constitué d'une suite de lettres et de chiffres qui se termine obligatoirement par un : (le caractère deux-points). L'expression ci-dessous appelle la méthode open: sur la classe WebBrowser. Il en résulte un appel système qui ouvre le navigateur web par défaut. La chaîne de caractères donnée en paramètre correspond à l'URL du site à ouvrir.
Le nom d'une méthode avec plusieurs paramètres est composé de plusieurs mots. Lors de l'appel, les paramètres sont placés chacun après le mot adéquat. Dans l'exemple suivant, la méthode to:by: est appelée sur l'entier 100 avec les paramètres 450 et 6. Cet appel permet de construire une collection d'entiers entre 100 et 450, par pas de 6.
Cette notation originale s'apparente à une phrase, aux : près. Elle a pour avantage d'améliorer la lisibilité du code. En effet, il est aisé de voir le rôle de chaque paramètre dans l'appel de la méthode, sans recourir à la définition de la méthode comme c'est le cas avec une notation parenthésée.
En l'absence de parenthèses, l'évaluation des expressions obéit aux règles suivantes. En premier, toutes les méthodes unaires sont évaluées, successivement de gauche à droite. Puis, vient le tour des méthodes binaires. En dernier lieu, les appels des méthodes à mots-clés sont effectués.
La figure 3 illustre ces règles de priorité au travers d'un exemple. Les numéros indiquent l'ordre d'exécution de chaque appel de méthode. Tout d'abord, les appels de méthodes unaires sont évalués de gauche à droite. Ainsi, la factorielle 1000 factorial est calculée, puis c'est la partie entière 3.14 floor, et ensuite la valeur absolue -5 abs. Viennent ensuite les appels des méthodes binaires, évalués toujours de gauche à droite, en utilisant les résultats des méthodes unaires. Ainsi, l'addition est évaluée, suivie de la puissance (**). Enfin, la méthode à mots-clés with:with: est appelée sur la classe Set. Le résultat est un ensemble qui a pour éléments les résultats des appels des méthodes binaires.
2.3 Un langage hautement réflexif
En tant que langage réflexif, Pharo permet d'écrire des programmes capables d’analyser leur propre structure, et même de s'automodifier en cours d'exécution. Il permet également de modifier sa propre sémantique, c'est-à-dire la manière d'exécuter les programmes. Deux caractéristiques permettent cette réflexivité : la pureté et la dynamicité.
2.3.1 La pureté
Les concepts du langage sont limités en nombre et uniformément utilisés, sans exception. C'est ce qui fait la réputation de Smalltalk comme LE langage à objets pur par excellence. Ainsi, hormis les variables, tout est objet. Tout objet peut réaliser des traitements en exécutant des méthodes. Et pour adapter la structure ou le comportement d'un objet, il suffit de modifier sa classe. Ainsi, les classes, les méthodes, les threads, l'ordonnanceur des threads, le compilateur, le contexte d'exécution sont tous des objets. Ils peuvent donc être manipulés et être modifiés au même titre que les objets applicatifs.
Pour illustrer la pureté de Pharo, considérons la création d'une sous-classe. Il y a différentes méthodes pour le faire. La plus simple est la méthode subclass: qu'il faut appeler sur la superclasse dont on veut hériter. Dans l'extrait de code ci-dessous, nous créons une sous-classe de Object. Le paramètre #Calculator est une chaîne de caractères unique qui correspond au nom de la nouvelle sous-classe.
Dans l'exemple suivant, nous utilisons une autre méthode pour créer une sous-classe de Calculator. Pour cela, nous fournissons quatre paramètres. Le premier paramètre est le nom de la sous-classe. Le second est une chaîne de caractères qui réunit les noms de deux variables d'instance. Ainsi, chaque instance disposera de ces 2 variables qui constitueront sa mémoire privée. Le troisième paramètre est une chaîne de caractères vide, car les instances de la classe MemoryCalculator ne disposent pas de variable partagée. Enfin, le dernier paramètre est le nom du paquetage auquel appartient la classe.
2.3.2 La dynamicité
La réflexivité de Pharo repose aussi sur sa dynamicité. Tout se passe à l'exécution. Le développement consiste à enrichir et adapter, in vivo, un ensemble d'objets. Le développement commence à partir d'un ensemble d'objets fondamentaux comme true, false, ainsi que des classes de base comme Object et Array. Le développeur ajoute progressivement les classes, les méthodes et les objets de son application. Il peut également inspecter et modifier les objets existants, y compris les classes de base. Cette dynamicité est favorable à une approche de développement agile, dirigée par les tests (Test Driven Development, TDD) [9]. Elle favorise également un cycle de développement court. En effet, les outils de débogage permettent de modifier le programme pendant son exécution, à l'endroit même où un bug est rencontré. Tout le contexte (variables, pile d'appels des méthodes) qui a débouché sur le dysfonctionnement est directement disponible et observable, ce qui facilite et accélère le déverminage.
Quand le programme réalise les fonctionnalités attendues, l'ensemble des objets impliqués est sauvegardé sur disque. Le résultat est un fichier image unique qui représente une copie instantanée du contenu de la RAM. En phase de production, le lancement de l'application est rapide, car les objets sont déjà initialisés. La machine virtuelle n'a plus qu'à charger l'image en mémoire vive.
Notion de machine virtuelle
Une machine virtuelle est un émulateur. Elle simule la présence de ressources matérielles spécifiques, comme le ramasse-miettes (Garbage Collector) qui libère automatiquement les cases mémoires qui ne sont plus utilisées.
De nombreux langages modernes comme Java, Lua, Pharo, et Python ont recours à des machines virtuelles. L'intérêt de cette technique est de faciliter la portabilité des programmes. Cela est résumé par le slogan write once, run everywhere. Un programme est compilé une seule fois. Le code binaire produit peut être exécuté sur toutes les cibles (couple matériel + système d'exploitation), pour lesquelles on dispose de machines virtuelles.
L'effort de portage de la machine virtuelle vers une nouvelle cible est mutualisé. Il bénéficie à tous les programmes développés à l'aide du même langage.
Une autre facette de la dynamicité réside dans la gestion des types. Le typage Pharo est strict, mais résolu dynamiquement. Le développeur se contente de nommer les variables. C'est à l'exécution que le type est vérifié. En cas d'erreur, la méthode doesNotUnderstand: est appelée. Par défaut, elle lève une exception. Cette méthode peut être redéfinie afin d'adapter la gestion des types. C'est notamment utile pour développer des applications réparties. La méthode doesNotUnderstand: est redéfinie pour capturer les appels de méthodes et les transmettre via le réseau, à des objets situés sur d'autres machines.
3. Les spécificités remarquables de Pharo
Comparé à Smalltalk, Pharo est porteur de nombreuses innovations [10]. Nous en listons quelques-unes dans cette partie. Nous aborderons ensuite plus en détail deux caractéristiques remarquables du langage : les traits et les slots.
3.1 Des nouveautés sur tous les plans
Sur le plan de l'environnement de développement, la palette d'outils de Pharo s'est progressivement enrichie de manière à améliorer la productivité des développeurs. Nous pouvons citer par exemple l'inspecteur d'objet adaptable, extensible et multivues GTInspector, le gestionnaire de projets sous Git Iceberg, l'outil de test DrTests, le gestionnaire d'images et de machines virtuelles Pharo Launcher, ainsi que l'analyseur de dépendances entre paquets.
Côté infrastructure, une nouvelle génération de machines virtuelles a été développée pour Pharo. Elles ont adopté une architecture 64 bits, ce qui permet aux applications d'exploiter pleinement les nouveaux matériels, et de manipuler de très grandes quantités de données. La vitesse est également au rendez-vous, puisque la machine virtuelle Pharo intègre un compilateur JIT (Just In Time Compiler). Ce JIT permet de générer à la volée du code machine natif pour accélérer les traitements les plus récurrents.
Du côté du langage à proprement parler, le compilateur Pharo a été totalement réécrit pour le rendre très modulaire. Il facilite la manipulation de l'arbre de syntaxe abstraite, et permet de générer un code intermédiaire de haut niveau, en plus de générer le code machine exécutable. Cette modularité a permis d'accélérer les opérations réflexives du nouveau noyau de Pharo. Ces opérations sont habituellement exploitées au niveau des outils de développement. Mais, leur puissance en Pharo est telle qu'elle a permis d'étendre le langage. Nous présentons ci-dessous deux exemples emblématiques de ces extensions, à savoir : le contrôle de la représentation mémoire des objets à l'aide des slots et l'héritage multiple à base de traits.
3.2 Les slots pour contrôler la représentation mémoire des objets
Dans la majorité des langages à objets, la représentation en mémoire de la structure des objets est inaccessible aux développeurs. A contrario, Pharo permet d'adapter cette structure au cas par cas, suivant les besoins applicatifs. Pour cela, les variables d'instance sont réifiées. Cela revient à représenter ces variables sous forme d'objets, appelés slots, qui régissent leurs propres cycles de vie (allocation, lecture, écriture...).
Pour illustrer l'impact sur l'allocation de la mémoire, prenons l'exemple d'un objet qui dispose de plusieurs variables d'instance référençant des booléens. Par défaut, chaque variable va occuper un mot mémoire. Mais, si l'on désigne ces slots comme instances de BooleanSlot, ils seront regroupés sous forme de bits d'un unique nombre entier, et ce, sans modifier le reste du code.
Les slots peuvent également influencer la gestion automatique de la mémoire. Par exemple, la référence stockée dans un slot instance de WeakSlot permet d'accéder à un objet tant qu'il est référencé par ailleurs. Si un objet n'est plus référencé que par des références faibles - comme celle du WeakSlot - il est considéré comme inutile. Le ramasse-miettes (Garbage Collector) le détruit, libérant ainsi automatiquement la mémoire.
Par ailleurs, grâce aux slots, il est possible d'associer des actions lors des lectures / écritures des variables d'instance. Par exemple, WriteOnceSlot permet de verrouiller sa valeur. Une seule affectation est possible.
Un autre usage du contrôle de l'écriture d'un slot est l'implémentation du schéma de conception observateur (Observer Design Pattern) [11]. En effet, un ObservableSlot notifie ses observateurs de tout changement de sa valeur. Cela rend le code plus lisible et évite les bogues dus aux oublis des notifications.
Cette liste est juste un aperçu des différentes classes de slots disponibles. Et pour couronner le tout, cet ensemble est extensible. On peut définir ses propres classes de slots et les utiliser au besoin.
Il est à noter que pour utiliser les slots, il a été nécessaire de revoir la création des classes de Pharo. Nous avons vu plus haut que celle-ci est réalisée en appelant une méthode sur la superclasse. Il a donc suffi de définir une nouvelle méthode de classe dont l'un des paramètres est un tableau avec les slots. L'exemple suivant illustre cette nouvelle API. Nous créons ici une classe de compteurs qui utilisent le slot HistorySlot. Chaque instance mémorise alors les 5 dernières valeurs de count.
3.3 Les traits pour un héritage multiple et dynamique
L'héritage entre classes Pharo est à la fois multiple et dynamique. Une classe peut hériter de multiples traits, en plus d'une superclasse. Cette relation est flexible. Elle peut évoluer en cours d'exécution d'un programme. Un exemple de l'utilité de cette caractéristique est la mise à jour à chaud. L'héritage peut être modifié sans redémarrer le programme.
Une version limitée du concept de trait a été introduite initialement pour les prototypes, avec le langage Self. Elle n'offrait aucun support pour gérer les conflits propres à l'héritage multiple. Ce problème a été résolu par une équipe de chercheurs, dont les pères de Pharo. L'idée a depuis été reprise - en partie seulement ! - dans d'autres langages à classes [12].
Les traits peuvent être vus comme une sorte de classes abstraites. Ils ne sont pas instanciables, mais ils permettent de définir du code réutilisable dans des classes appartenant à différentes hiérarchies. Dans la plupart des langages, un trait ne peut définir que des méthodes. Pharo va plus loin en permettant de créer des traits pouvant définir également des slots.
La figure 4 donne un exemple d'héritage multiple illustrant l'usage des traits de Pharo. Il s'agit d'une hiérarchie de classes de véhicules. La superclasse racine (Vehicle) définit la structure et le comportement communs à toutes sortes de véhicules. L'héritage simple suffit pour réutiliser ce code dans les classes des voitures (Car) et des avions (Plane). Cependant, l'héritage multiple est indispensable pour définir des classes de voitures modernes (ModernCar) et d'avions modernes (ModernPlane). En effet, pour éviter la duplication de code, ces deux classes réutilisent le code défini dans deux traits. L'un (TGpsVehicle) définit le code de la localisation et du calcul d'itinéraire à l'aide d'un GPS. L'autre trait (TSelfDrivingVehicle) fournit le code pour gérer le pilotage automatique.
Une des caractéristiques fondamentales des traits de Pharo est qu'ils disposent de différents opérateurs [13]. Ils permettent aux développeurs de gérer finement les conflits d'héritage. Il y a conflit quand il y a collision de noms. C'est le cas quand des slots ou des méthodes différentes portant les mêmes noms sont hérités par une classe à partir de traits différents. Deux opérations permettent aux développeurs d'intervenir pour résoudre de tels conflits :
- Le filtrage : permet de rejeter certains slots ou méthodes lors de la transmission à la sous-classe. Ces éléments ne sont pas hérités et donc ne sont pas disponibles dans les instances de la sous-classe.
- Le renommage : permet de renommer des slots ou des méthodes avant de les hériter. Ils apparaissent sous un nouveau nom dans la sous-classe.
Il faut noter que ces opérations sont locales. Elles impactent uniquement la sous-classe particulière à laquelle elles sont appliquées. Ainsi, il est possible d'avoir des règles différentes pour la résolution des conflits d'héritage pour différentes sous-classes.
Comme indiqué plus haut, les traits et leurs opérateurs ont été introduits en Pharo grâce à la réflexivité du langage. En effet, les traits constituent une sorte de classes. Or, les classes sont des objets, donc instances d'autres classes : les métaclasses. L'introduction des traits a ainsi été réalisée en définissant de nouvelles métaclasses.
La qualité du code Pharo
Le consortium Pharo est le garant de la qualité de ce langage libre. Les entreprises membres assurent un ancrage dans le « terrain ». Les orientations de Pharo répondent ainsi à des besoins de développeurs professionnels. Les laboratoires de recherche se chargent quant à eux de pourvoir la communauté en résultats de recherches sources d'innovation.
Le développement de Pharo suit un processus dirigé par les tests, organisé autour de GitHub. Les différentes bibliothèques, les outils, les éléments d'infrastructure et même les briques du noyau du langage même sont couverts par des tests. L'ajout d'une nouvelle fonctionnalité ou d'un correctif se traduit par une requête d'intégration dans GitHub (Pull Request). L'intégration est conditionnée par une double validation. D'abord, le serveur d'intégration continue vérifie la non-régression. Une fois qu'on est sûr que tous les tests existants sont validés, un intégrateur humain fait une revue du nouveau code proposé. Ce processus permet d'assurer une qualité croissante du code, tout en garantissant la fiabilité de Pharo.
4. Sélection de logiciels libres développés en Pharo
Pharo est utilisé pour réaliser de multiples logiciels libres, produits par une communauté très dynamique. Ces projets sont très divers. Ils touchent différentes branches de l'informatique [14]. Nous en listons quelques-uns ci-dessous, groupés par thème.
4.1 Visualisation de données et animation graphique
L'analyse de grandes quantités de données brutes est un challenge. Une manière d'y faire face consiste à recourir à des représentations graphiques. C'est à cette famille de solutions qu'appartient le projet Roassal [15]. Il permet de visualiser, sous la forme de graphiques interactifs et animés, des données fournies dans des formats variés tels que XML, JSON et CSV.
Chaque jeu de données est modélisé en interne dans Roassal comme un graphe. Les nœuds du graphe correspondent aux données liées à une même entité. Les arcs représentent les relations entre les entités. Prenons l'exemple de données économiques mondiales. Les nœuds du graphe représenteraient les données spécifiques aux pays comme le nombre d'habitants. Les échanges commerciaux entre pays seraient les arcs du graphe.
Étant donné un graphe, Roassal visualise les nœuds à l'aide de formes géométriques. Les données associées à chaque nœud se manifestent au travers des dimensions de l'élément graphique correspondant, de sa couleur ainsi que de l'intensité de cette dernière. Les arcs du graphe sont traduits sous forme de lignes qui connectent les éléments graphiques. L'épaisseur d'une ligne et sa couleur varient suivant l'information attachée à l'arc représenté. La figure 5 donne trois exemples différents de visualisations réalisées à l'aide de Roassal.
Enfin, Roassal offre une panoplie de stratégies de mise en page du graphe représentant un jeu de données. L'utilisateur peut ainsi adapter les caractéristiques graphiques des éléments, ainsi que leur disposition, de manière à mieux mettre en exergue certaines informations.
Les capacités d'interaction offertes par Roassal contribuent à faire face à la masse d'informations. Les données secondaires peuvent être cachées pour n'être révélées que partiellement, en réaction à une action de l'utilisateur, comme un clic. Il est ainsi possible de produire des outils d'exploration de données, directement utilisables par les experts d'un domaine particulier.
4.2 Intelligence artificielle et apprentissage automatique
L'intelligence artificielle étant un thème qui a le vent en poupe, ce n’est donc pas une surprise qu'elle soit l'objet de divers livres et projets Pharo [16]. En voici deux exemples.
- La bibliothèque NEAT [17] permet de réaliser des réseaux de neurones évolutifs. Elle se base sur le principe de l'évolution neurale. Suivant cette approche, l'apprentissage se traduit par la révision de la topologie du réseau de neurones, en plus des poids des connexions entre neurones.
- La bibliothèque Ngram [18] est utilisée pour le traitement du langage naturel et la reconnaissance vocale. Elle permet d'effectuer une analyse statistique de texte et de prédire la suite logique. Ce type d'analyse est typiquement utilisé dans les claviers des smartphones, pour proposer un mot après la saisie de quelques lettres.
4.3 Internet des Objets et Robotique
Comme son nom l'indique, le projet Pharo IoT (Internet of Things) [19] cible le développement d'applications pour l'Internet des Objets. À cet effet, il offre une plateforme et des outils pour réaliser des programmes destinés à s'exécuter sur les fameuses cartes Raspberry Pi.
Le support de la programmation interactive (Interactive programming) [20] permet à Pharo IoT de se démarquer par rapport aux autres solutions. Le développeur travaille sur un ordinateur classique, et bénéficie de tout le confort de l’environnement de développement de Pharo. Et, au fur et à mesure des ajouts et modifications du code, le programme est actualisé sur la Raspberry Pi. Ainsi, le débogage des applications est effectué de manière interactive. Le développeur voit sans délai les effets de ses changements. Son travail est facilité, parce qu'il reste focalisé sur le contexte des bogues identifiés.
La flexibilité de l'inspecteur de Pharo est également exploitée pour simplifier le débogage. Pharo IoT introduit en effet une extension qui permet d'afficher les différents ports de la carte Raspberry Pi et d'en modifier l'état directement. Cela permet d'expérimenter différentes configurations, tester les périphériques raccordés, sans avoir à modifier le programme.
Il convient de noter que Pharo IoT cible la carte Raspberry Pi. Il peut donc être utilisé pour des applications autres que l'Internet des Objets. La robotique en est un exemple des plus captivants [21].
La robotique mobile et autonome est également au cœur du projet PhaROS [22]. Celui-ci a permis différentes réalisations, mettant en œuvre des robots mobiles et autonomes dans différents contextes. En effet, PhaROS permet d'interfacer des programmes Pharo avec l'intergiciel ROS (Robot Operating System) [23]. Ce dernier est un standard de fait. À ce titre, il donne accès à de nombreux robots, tant industriels que destinés au monde de la recherche. Il existe également de nombreuses bibliothèques libres compatibles ROS. Elles représentent donc autant de briques réutilisables pour la production de logiciels de contrôle de robots à base de PhaROS.
4.4 Le Web, côté serveur
Seaside [24] est le plus populaire des frameworks de développement web en Pharo. Une de ses caractéristiques majeures est la modularité. Les applications sont constituées de composants imbriquables : un composant peut contenir d'autres composants. Par ailleurs, il n'y a pas de notion de page. En revanche, n'importe quel composant peut être utilisé pour générer une page en réponse à une requête.
Un composant joue le rôle de contrôleur au sens du fameux schéma de conception MVC (Model-View-Controller). Il a la responsabilité de générer le code HTML de la vue, et de le lier avec le code du modèle. Ce lien est codé sous la forme de callbacks, attachés aux actions de l'utilisateur sur les éléments HTML générés.
Une dernière caractéristique importante de Seaside est la gestion simplifiée des opérations longues nécessitant plusieurs pages d'interaction avec les utilisateurs. C'est par exemple le cas d'un achat en ligne où on valide le panier (page 1), puis on choisit l'adresse et le mode de livraison (page 2) et enfin, on passe au paiement (page 3). Seaside fait une utilisation originale du concept de continuation [25]. Il permet de décrire de telles séquences de manière naturelle, ce qui reflète bien l'aspect séquentiel d'un tel processus. Le recours aux continuations permet également de gérer les cas où les utilisateurs appuient sur le bouton retour (back) du navigateur web.
4.5 Applications mobiles pour iOS et Android
Le projet PharoJS [26] est né de la volonté de mettre la puissance de Pharo au service des développeurs d'applications mobiles. Le codage et la mise au point sont effectués en Pharo. Pour le passage en production, le code Pharo de l'application est totalement transformé en JavaScript. Ce code JavaScript, accompagné de fichiers HTML et CSS peut alors être transformé en application mobile grâce aux outils du projet Cordova [27], de la fondation Apache.
Le framework PharoJS, son middleware, ainsi que son jeu d'outils ont été pensés pour encourager une approche de développement dirigée par les tests TDD (Test Driven Development). Au lancement d'un test, un lien est établi entre les objets Pharo, les objets DOM, ainsi que d'éventuelles librairies JavaScript utilisées. Pour cela, PharoJS ouvre la page HTML de départ de l'application dans un navigateur web. Le middleware se charge d'établir la connexion pour acheminer les communications entre les objets JavaScript et les objets Pharo en cours de développement. Ainsi, des objets des deux mondes peuvent appeler des méthodes des uns et des autres, suivant les besoins du test. Pour aider à la mise au point, PharoJS intègre un inspecteur dédié aux objets JavaScript. Ainsi, il est possible de consulter leur état et de le modifier, de la même manière que pour les objets Pharo.
Cette approche a été utilisée avec succès pour produire des applications mobiles [28], disponibles dans l'App Store d'Apple pour iPhone et iPad, ainsi que dans le Play Store de Google pour les équipements Android. Cela dit, l'infrastructure de PharoJS est générique. Elle peut donc être utilisée également pour d'autres types de réalisations, comme des clients web ou des applications NodeJS.
5. Exemples de produits commerciaux d'envergure basés sur Pharo
Bien que très ancré dans le monde du libre, Pharo est également utilisé au quotidien par des entreprises. Dans cette partie, nous décrivons quelques-uns de ces produits commerciaux les plus significatifs [29].
5.1 Architecture à microservices pour la mobilité
La société allemande Zweidenker développe pour le compte d'Airbus un projet de gestion de la mobilité. Celui-ci est bâti sur une architecture à base de microservices, qui communiquent via une API REST. Chaque service est un programme Pharo, déployé dans un conteneur Docker. Le tout est hébergé par une grappe de machines GNU/Linux.
Afin de supporter des pics de charge de plus de 10000 utilisateurs, les différents services sont distribués sur plusieurs serveurs. L'ensemble est géré par le logiciel Docker Swarm [30], également réalisé en Pharo par Zweidenker.
5.2 Place de marché en ligne pour les équipements professionnels
ALLSTOCKER est la plus grande place de marché virtuelle en Asie, pour les équipements industriels et de chantier. La totalité de cette solution d'enchères en ligne est développée en Pharo. Ainsi, le serveur web exploite entre autres le framework Seaside, présenté dans la partie 4.4.
Afin de gérer les transactions, ALLSTOCKER s'appuie sur le framework Pharo Glorp [31]. Celui-ci constitue une solution fiable pour interfacer avec une base de données relationnelle PostgreSQL.
5.3 Prototypage d'interfaces d'équipements militaires
Le grand groupe de défense français Thalès produit du matériel militaire dont la réalisation est longue et coûteuse. Dans ce cadre, Pharo est utilisé pour simuler les nouvelles réalisations [32], afin d'en évaluer la pertinence et l'ergonomie.
Cette solution s'appuie sur la réalité virtuelle [33]. Elle met en œuvre Woden [34], un moteur graphique 3D développé en Pharo. Ainsi, il est possible de réaliser des environnements immersifs que les utilisateurs peuvent explorer en temps réel.
5.4 Télécommunications mobiles et IoT
La société canadienne Telna offre différents produits destinés aux professionnels des télécommunications. Leurs logiciels développés en Pharo gèrent des familles de protocoles de téléphonie tels que SS7 ou GTP. Le SS7 (Signaling System No 7) [35] est une famille de protocoles qui permettent d'établir les connexions. GTP (General packet radio service Tunneling Protocol) [36] regroupe des protocoles destinés à acheminer des communications IP sur les réseaux mobiles 3G et 4G.
Les solutions pour l'Internet des Objets (IoT) produites par Telna peuvent être déployées sur plus de 800 réseaux mobiles. Ainsi, Pharo se trouve au cœur d'une plateforme de communication liant des équipements qui peuvent être répartis sur plus de 200 pays et territoires à travers le monde.
Conclusion
« La simplicité est la sophistication suprême. »Léonard de Vinci
Smalltalk a, sans conteste, marqué de son empreinte l'industrie informatique. Ses concepteurs ont su capturer la substantifique moelle de la programmation par objets. Cet ensemble réduit de concepts de haut niveau, combiné avec une syntaxe simple et uniforme, a un double intérêt. D'une part, l'apprentissage se limite essentiellement à une prise en main des bibliothèques et des outils de développement. D'autre part, les développeurs peuvent se focaliser sur l'analyse du problème et sur la logique de la solution réalisée.
Pharo a été bâti sur ces solides fondations. Sa communauté, équilibrée entre industriels et académiques, constitue une belle source d'innovations en génie logiciel, comme en témoignent de nombreuses nouveautés. Ces évolutions ont pu être introduites grâce aux capacités inédites de Pharo en matière de réflexion et de métaprogrammation.
Références
[1] Page Wikipédia de Smalltalk : https://fr.wikipedia.org/wiki/Smalltalk
[2] Site web de Pharo : http://pharo.org
[3] Liste d'entreprises et d'établissements académiques membres du consortium Pharo : http://consortium.pharo.org/
[4] Site d'ESUG, groupe européen des utilisateurs de Smalltalk : http://esug.org
[5] A. KAY, « The Early History of Smalltalk », History of programming languages, janvier 1996, p. 511 à 598 : http://esug.org/data/HistoricalDocuments/Smalltalk80/SmalltalkHistory.pdf
[6] Numéro hors-série de Byte Magazine dédié à Smalltalk-80, août 1981 : https://archive.org/details/byte-magazine-1981-08/mode/2up
[7] Carte postale illustrant la syntaxe Pharo : https://github.com/pavel-krivanek/pharoMaterials/tree/master/postcard
[8] Flyer Pharo avec cheat sheet : https://files.pharo.org/media/flyer-cheat-sheet.pdf
[9] Wikipédia, « Développement dirigé par les tests » : https://fr.wikipedia.org/wiki/Test_driven_development
[10] Page listant les principales caractéristiques de Pharo : http://pharo.org/features
[11] Wikipédia, schéma Observer Design Pattern : https://en.wikipedia.org/wiki/Observer_pattern
[12] Wikipédia, les différentes déclinaisons des traits dans différents langages : https://en.wikipedia.org/wiki/Trait_(computer_programming)
[13] Documentation en ligne sur les traits dans Pharo : https://github.com/pharo-open-documentation/pharo-wiki/blob/master/General/Traits.md
[14] Sélection de projets libres réalisés par la communauté Pharo : https://github.com/pharo-open-documentation/awesome-pharo
[15] Documentation en ligne de Roassal, moteur de visualisation graphique de données : http://agilevisualization.com/
[16] Liste de projets Pharo sur l'apprentissage automatique : https://github.com/pharo-ai/awesome-pharo-ml
[17] Page GitHub de la bibliothèque NEAT sur les réseaux de neurones évolutifs : https://github.com/bergel/NEAT
[18] Page GitHub de la bibliothèque Ngram pour le traitement de langage naturel : https://github.com/pharo-ai/Ngram
[19] Site web de Pharo IoT : https://www.pharoiot.org/
[20] Wikipédia, « Programmation interactive » : https://en.wikipedia.org/wiki/Interactive_programming
[21] Vidéo d'un robot basé sur Pharo IoT : https://www.youtube.com/watch?v=1j5WSCkIiKk
[22] Wiki du projet robotique PhaROS : https://github.com/CARMinesDouai/pharos/wiki
[23] Site web du middleware robotique ROS : https://www.ros.org/
[24] Page GitHub du framework de développement web Seaside : https://github.com/seasidest/seaside
[25] Wikipédia, concept de Continuation : https://fr.wikipedia.org/wiki/Continuation
[26] Site web de PharoJS : http://pharojs.org/
[27] Site web de Cordova : https://cordova.apache.org/
[28] Annuaire d'applications réalisées avec PharoJS : https://pharojs.org/successStories.html
[29] Exemples de projets réalisés en Pharo : http://pharo.org/success
[30] Présentation de la solution Docker Swarm : https://www.slideshare.net/zweidenker/docker-and-pharo-zweidenker
[31] Livre, avec PDF gratuit, sur le mapping objet-relationnel en Pharo avec Glorp : http://books.pharo.org/booklet-Glorp/
[32] Présentation du prototypage d'interfaces utilisateur au sein du groupe Thalès : https://www.slideshare.net/esug/ui-prototyping-with-smalltalk-at-thales
[33] Vidéo d'une démonstration de réalité virtuelle avec Pharo au sein du groupe Thalès : https://youtu.be/b4nNtN7XBi8
[34] Dépôt GitHub du moteur graphique 3D Woden : https://github.com/woden-engine/woden
[35] Wikipédia, la famille de protocoles SS7 : https://fr.wikipedia.org/wiki/Signaling_System_7
[36] Wikipédia, la famille de protocoles GTP : https://fr.wikipedia.org/wiki/GPRS_Tunnelling_Protocol
Pour aller plus loin
- MOOC Pharo - Formation en ligne, gratuite en français et en anglais : http://mooc.pharo.org/
- Pharo Books - Livres, avec PDF gratuits, sur Pharo et les projets associés : http://books.pharo.org/
- Weekly Pharo - Blog sur l'actualité de Pharo : https://pharoweekly.wordpress.com/
- Pharo Wiki - Documentation Pharo en ligne : https://github.com/pharo-open-documentation/pharo-wiki
Remerciements
Cet article a bénéficié des commentaires avisés des personnes suivantes :
- Luc Fabresse, Professeur à l'IMT Lille Douai ;
- Oliver Auverlot, Délégué à la Protection des Données au laboratoire CRIStAL ;
- Stéphane Ducasse, Directeur de recherche à l'Inria Lille.