Mes enfants (7 & 8 ans) me voient collé à mon micro plus qu'il ne faudrait. Un jour, ils ont souhaité que je leur explique la "programmation". C'est ma fille qui a commencé à 5 ans avec du Ruby, mais les concepts ne sont pas forcément simples, qui plus est quand à l'école ils n'ont pas encore toutes les notions de math ou de logique, sans parler de l'anglais! Donc, même si ça les attire (imaginez un truc qui compte à votre place!), ils peinent un peu (il doit aussi y avoir la pédagogie de leur père en cause).
En étudiant Golo, et au fur et à mesure de ma compréhension (merci @jponge pour ses patientes explications), je me suis dit "et si je me servais de Golo pour apprendre le dév à mes rejetons ?". Du coup je leur ai demandé de commencer à me "spécifier" un langage sur papier (vu qu'ils sont punis de télé, de tablette, d'ordi pour 4 semaines, ça les occupe bien). A ma grande surprise, ils sont en train de le faire, et en plus ils avancent bien et ils ont choisi le thème du Zoo.
Je me retrouve donc dans une situation, où il va falloir faire ce langage pour enfants! Donc ce matin, j'ai voulu qualifier la faisabilité de ce projet (et ma capacité à le faire).
Et bien, je vous l'affirme, Golo est un parfait "générateur" de langage!!! ... et je ne vais pas passer pour un incapable auprès de mes 2 dictateurs préférés :).
Voci donc la recette du jour (et je m'amuse comme un petit fou) pour créer un langage, et se la jouer grave ensuite :)
Pour ceux qui ne connaisse pas Golo, c'est par ici http://golo-lang.org/.
Allez, c'est parti, nous allons voir :
Pas d'inquiétude, c'est ULTRA-FACILE.
Commencez donc par cloner le repository de golo :
git clone https://github.com/golo-lang/golo-lang.git
vous disposez maintenant de tout ce qu'il faut pour vous amuser :)
... je vous rappelle que le thème, c'est le Zoo.
Nous allons donc déjà ajouter simplement de nouveaux "objets" à Golo, que nous allons implémenter dans le package gololang.zoo, pour cela dans le projet Golo, vous devez, au sein du package gololang, créer un package zoo :

Ensuite, nous développons quelques classes et interfaces qui seront la base de notre langage pour enfants :

Animal.java :
package gololang.zoo;
public interface Animal {
public void sePositionne(Integer x, Integer y);
}
Position.java
package gololang.zoo;
public class Position {
private Integer _x = null;
private Integer _y = null;
public Integer x() { return _x; }
public Position x(Integer value) { _x = value; return this; }
public Integer y() { return _y; }
public Position y(Integer value) { _y = value; return this; }
public Position(Integer _x, Integer _y) {
this._x = _x;
this._y = _y;
}
public Position() {}
}
Mammifere.java
package gololang.zoo;
public class Mammifere implements Animal {
private Position _position = null;
private String _nom = null;
@Override
public void sePositionne(Integer x, Integer y) {
_position.x(x);
_position.y(y);
}
public Mammifere(String nom) {
this._nom = nom;
}
public Mammifere(String nom, Position position) {
this._position = position;
this._nom = nom;
}
public String nom() {
return _nom;
}
public Animal nom(String nom) {
this._nom = nom;
return this;
}
public Position position() {
return _position;
}
public Animal position(Position _position) {
this._position = _position;
return this;
}
}
Lion.java
package gololang.zoo;
public class Lion extends Mammifere {
public Lion(String nom) {
super(nom);
}
public Lion(String nom, Position position) {
super(nom, position);
}
public String rugir() { return "Groooooaaaaaarrrrr!!!";}
public String rugir(String paroles) { return paroles + "Groooooaaaaaarrrrr!!!";}
}
Nous pouvons déjà recompiler Golo. Vous vous positionnez dans le répertore /golo-lang et "lancez" la commande suivante :
rake special:bootstrap
Au bout de quelques instants (quand c'est terminé donc) vous obtenez le répertoire /<chez_vous>/golo-lang/target/golo-0-preview3-SNAPSHOT-distribution/golo-0-preview3-SNAPSHOT (en fonction des version les noms peuvent changer) qui contient un Golo utilisable. Faites donc pointer vos variables d'environnement dessus :
GOLO_HOME=/<chez_vous>/golo-lang/target/golo-0-preview3-SNAPSHOT-distribution/golo-0-preview3-SNAPSHOT; export GOLO_HOME
export PATH=$PATH:$GOLO_HOME/bin
Nous allons maintenant créer un script zoo.golo :
module zoo
import gololang.zoo.Lion
import gololang.zoo.Position
function main = |args| {
let leo = Lion("Léo le lion")
println(leo:nom())
let pos = Position(4, 7)
leo:position(pos)
println("x: " + leo:position():x() + " y: " + leo:position():y())
println(leo:rugir())
println(leo:rugir("Je suis " + leo:nom() + "... "))
}
Lancez : gologolo zoo.golo, et ... :
Léo le lion
x: 4 y: 7
Groooooaaaaaarrrrr!!!
Je suis Léo le lion... Groooooaaaaaarrrrr!!!
N'êtes vous pas déjà un peu fier, là ?
Le projet Golo a une classe très pratique Predefined.java dans le package gololang, qui permet d'ajouter des sortes de mots-clé. C'est là que vous avez l'implémentation de println() par exemple. Pour résumer, Predefined.java comporte un ensemble de méthodes statiques réutilisables en Golo comme mots-clé. Voyon donc comment ajouter des mots-clé :
Je n'aime définitivement pas concaténer des chaînes de caractères, donc à la place de "x: " + leo:position():x() + " y: " + leo:position():y() j'aimerais pouvoir écrire "x: %s y: %s", leo:position():x(), leo:position():y() ou à la place de "Je suis " + leo:nom() + "... " avoir "Je suis %s ... ",leo:nom(). Allons-y!
Ajoutez les 2 méthodes ci-dessous dans Predefined.java :
formater:
public static String formater(String message, Object... args) {
return String.format(message, args);
}
afficher:
public static void afficher(String message, Object... args) {
System.out.println(formater(message, args));
}
Maintenant, compilez à nouveau Golo (rake special:bootstrap) et modifiez votre script zoo.golo de la manière suivante :
module zoo
import gololang.zoo.Lion
import gololang.zoo.Position
function main = |args| {
let leo = Lion("Léo le lion")
afficher("Mon est %s", leo:nom())
let pos = Position(4, 7)
leo:position(pos)
afficher("x: %s y: %s", leo:position():x(), leo:position():y())
afficher("x: %03d y: %03d", leo:position():x(), leo:position():y())
afficher("Léo va rugir : %s"
, leo:rugir(
formater("Je suis %s ... ",leo:nom())
)
)
}
Lancez : gologolo zoo.golo, et ... :
Mon est Léo le lion
x: 4 y: 7
x: 004 y: 007
Léo va rugir : Je suis Léo le lion ... Groooooaaaaaarrrrr!!!
Et là, au sentiment de fierté précédent, s'ajoute un début d'impression, voire d'intelligence "inhabituelle" ;) De là à dire que Golo rend intelligent, il n'y a qu'un pas ... que je franchis allègrement :)))
Depuis un moment, vous devez vous dire que ma manie de mettre du français dans mon code est un peu bizarre, voire c'est mal, ça ne se fait pas, je suis un hérétique! Je vous rappelle que le besoin initial, c'est apprendre la programmation à de jeunes enfants, alors si en plus, ils doivent apprendre l'anglais, ils vont devenir chèvres.
Avez-vous essayé d'expliquer le concept de variable et valeur de variable à des enfants de 8 ans et moins de 8 ans (5 ans!) ? En ce qui me concerne, la notion de boîte d'alumettes, marche pas mal (avec un nombre d'alumettes dans la boîte) ou la notion de case peut aussi faire l'affaire.
Donc à la place de let leo = Lion("Léo le lion") je voudrais la_case leo = Lion("Léo le lion") et comme j'ai pris de l'assurance dans les paragraphes précédents, à la place de if {} else {}, je veux si {} sinon {}. Voui Monsieur!
Eh bien, c'est extrêment facile :
Allez faire un tour dans le projet Golo par ici : /src/main/jjtree/Golo.jjt :
< LET: "let" > et remplacez par < LET: ("let" | "la_case") > (vous pourrez ainsi continuer à utiliser let)< IF: "if" > et remplacez par < IF: ("if" | "si") >< ELSE: "else" > et remplacez par < ELSE: ("else" | "sinon") >Compilez à nouveau Golo (rake special:bootstrap), et maintenant vous pouvez écrire des choses comme celle-ci :
module zoo
import gololang.zoo.Lion
import gololang.zoo.Position
function main = |args| {
la_case leo = Lion("Léo le lion")
la_case pos = Position(4, 7)
leo:position(pos)
si leo:position():x() > 3 {
afficher("x: %s (est supérieur à 3) y: %s", leo:position():x(), leo:position():y())
} sinon {
afficher("x: %s (est inférieur à 3) y: %s", leo:position():x(), leo:position():y())
}
}
Je n'ai maintenant plus qu'à tester avec mes petits bouts. Je vous en donnerais des nouvelles. De votre côté, plus d'excuses pour vous mettre à Golo, augmenter Golo ... ;)
42. ;)
Golo est fait pour utiliser des classes Java. Si vous manipulez des classes qui implémentent des interfaces, il est tout à fait possible de "greffer" des comportements à vos méthodes de classes (celles déclarées dans une interface) grâce à import java.lang.reflect.Proxy. Voyons donc comment faire:
Imaginez une interface java "Toon":
package org.k33g;
public interface Toon {
public String hello(String message);
public String speak(String message);
}
Imaginez une classe java qui implémente Toon :
package org.k33g;
public class TinyToon implements Toon {
private String name = null;
public TinyToon(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String hello(String message) {
return "HELLO I'M "+this.name+" : "+ message;
}
@Override
public String speak(String message) {
return "BLABLA BY "+this.name+" : "+ message;
}
}
Il est possible de créer un proxy de TinyToon, implémentant donc Toon et ce dynamiquement :
Ce peut être très pratique, si vous avez des traitements (méthodes) qui "attendent" des objets de type Toon, et que vous souhaitez ajouter des comportements aux méthodes (ce n'est possible que pour les méthodes déclarées dans l'interface implémentée par la classe) :
Dans un 1er temps, si nous utilisons tout simplement TinyToon :
import org.k33g.TinyToon
function main = |args| {
let toon = TinyToon("Babs")
println(toon:hello("HI !!!"))
println(toon:speak("IT'S SO CUTE"))
}
Nous obtiendrons à l'exécution la sortie suivante :
HELLO I'M Babs : HI !!!
BLABLA BY Babs : IT'S SO CUTE
Maintenant, nous allons créer notre proxy de TinyToon :
Nous allons maintenant utiliser Proxy.newProxyInstance() :
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Proxy
import org.k33g.TinyToon
function main = |args| {
let toon = TinyToon("Babs")
let toonProxy = Proxy.newProxyInstance(
toon:getClass():getClassLoader(),
toon:getClass():getInterfaces(),
(|proxy, method, args...| {
println("You've called : " + method:getName())
let result = null
if method:getName() is "hello" {
println("hello from proxy")
result = toon:hello(args:get(0))
}
if method:getName() is "speak" {
println("speak from proxy")
result = toon:speak(args:get(0))
}
return result
}):to(InvocationHandler.class))
println(toonProxy:hello("HI !!!"))
println(toonProxy:speak("IT'S SO CUTE"))
}
Remarquez que nous "castons" notre closure |proxy, method, args...| { ... } en InvocationHandler, en utilisant ():to(InvocationHandler.class).
Nous obtiendrons à l'exécution la sortie suivante :
You've called : hello
hello from proxy
HELLO I'M Babs : HI !!!
You've called : speak
speak from proxy
BLABLA BY Babs : IT'S SO CUTE
Et voilà, vous venez de faire de l'AOP avec Golo. Ce qui vous démontre une fois de plus la puissance de ce langage.
Vous trouverez ici une version "générique" réutilisable d'un proxy dynamique : https://github.com/k33g/DynoGolo.
42. ;)
à venir : comment modifier directement Golo. Stay tuned
Je n'ai qu'un mot à l'esprit qui me vient à l'esprit après ces 2 journées (25 & 26 avril), c'est Merci !. J'ai en effet passé 2 jours (+1 soirée) à m'aérer les méninges, à avoir de nouvelles idées ...
Mix-IT c'est comme un réseau social "EN VRAI" ou "grandeur nature", où l'on rencontre enfin des gens que l'on ne côtoyait que virtuellement, où l'on a l'occasion de rencontrer de nouvelles personnes riches d'idées, de projets, de technos, ...
Mix-IT c'est aussi une équipe de "fous" qui a fait un travail formidable pour faire venir des speakers renommés et de qualité, pour donner leur chance à des speakers moins connus (mais de qualité aussi) et les réunir tous au milieu d'une foule de "spectateurs" aux yeux brillant d'excitation. On obtient ainsi un groupe de geeks "chasseurs de stickers" (speakers & spectateurs) qui ne se prennent pas la tête, qui assistent à des présentation, débattent dans les couloirs, ...
2 jours sur une autre planète ... accueillante, donc encore merci les gars (filles & garçons) :)
Sinon, côté prez, qu'est-ce qui m'a le plus plu ?