Blog Arolla

Ceylon : un java moderne et sans legacy

J'ai récemment eu l'occasion d'assister à la conférence "Ceylon Tour Paris 2014".

Je me suis intéressé à ce langage pour certains de ses objectifs :

  • Être tellement simple à lire qu'un développeur connaissant l'Orienté Objet peut le comprendre sans apprendre le langage.
  • Disposer d'un typage statique implicite.

Il présente d'autres caractéristiques intéressantes que j'ai découvertes à la conférence "Ceylon Tour Paris 2014" (et qui sont présentées sur la page d’accueil de leur site). Par exemple, il doit :

  • Tourner sur la JVM et dans un moteur Javascript
  • Être un modèle de composant (du modulaire qui montre les contrats et cache les implémentations)

Je me suis inscrit à cette conférence parce que Ceylon mérite que l'on s'y essaye et pour être honnête parce qu'elle était gratuite...

L’écart entre Java 6,7 ou 8 et Ceylon

Ceylon ressemble à ce que serait Java s'il était à nouveau écrit aujourd'hui. Ils ont les mêmes cibles d'usage : être un langage Objet "généraliste", plutôt facile d'accès. Ceylon a quelques ambitions supplémentaires : il peut tourner sur la JVM mais aussi dans un moteur Javascript comme nos chers clients web.

Modèle de données

Depuis l'apparition de Java, nous avons vu des logiciels de gestion :

  • manipulant des documents qui factorisent leurs données dans des bases relationnelles,
  • communiquant leurs données en xml,
  • mettant à jour leurs pages web avec du Json...

Là où Java ne propose que des beans avec des champs et des relations qui sont inadaptées aux bases relationnelles, Ceylon propose d'écrire simplement des hiérarchies de données. Les arborescences DOM HTML en sont un bon exemple. Le système de typage est statique, fort et implicite. Du coup, on peut écrire des structures de données complexes sans s'arracher les oreilles ;-). Ces hiérarchies de données sont des déclaration d'instances imbriquées que l'on trouve dans la page qui présente les arguments nommés. Tout en bas de cette page, l'avant dernier chapitre montre un exemple d'utilisation du module html :

/* Attention, ce n'est pas un DSL, c'est du code Ceylon typé et compilé en bytecode java ou javascript */
Html {
  doctype = html5;
  Head {
    title = "Ceylon: home page";
    Link {
      rel = stylesheet;
      type = css;
      href = "/styles/screen.css";
      id = "stylesheet";
    };
  };
  Body {
    H2 ( "Welcome to Ceylon ``language.version``!" ),
    P ( "Now get your code on !" );
  };
};

Modèle de flux d’exécution (threads et concurrence)

Depuis l'apparition de Java, nous avons vu la volumétrie des données exploser, et les capacités de calculs se multiplier grâce aux processeurs multi-cœurs. Cela soulève deux problèmes que Java ne peut résoudre : avoir des structures de données naturellement "thread safe" et une capacité à manipuler les traitements comme on traite les données.

Ceylon pour cela propose que ses "variables" soient immutables par défaut et mutables par annotation. Pour être clair, c'est la notion inverse en Java où les variables sont mutables par défaut en immutables avec le mot clé "final". Ainsi Ceylon manipule des "valeurs" :

value rndValue = Math.random(); // rndValue est immutable.
variable value rndIteration = Math.random(); // rndIteration peut être assignée avec une nouvelle valeur.

Pour les amateurs de DDD et/ou de programmation fonctionnelle, sachez que les Value Object et les paramètres immutables sont la norme.

Pour Ceylon, tout a une référence, y compris les fonctions que l'on peut assigner à des "valeurs", passer en paramètres ou retourner par nos fonctions. On peut ainsi propager des traitements d'une classe à une autre, voire d'un thread à un autre sans le formalisme des Single Abstract Method ou des classes implémentant "Runnable". Les "compréhensions" en sont une conséquence naturelle et pourtant exotique pour moi.  Une "compréhension" est l'initialisation d'une collection, map ou tableau, avec une expression retournant une collection du bon type... La collection peut donc être une map et l'expression être des fonctions de filter et/ou reduce. Grâce à ces compréhensions, Ceylon n'a pas besoin d'une nouvelle syntaxe lambda, ni de java 8 pour fournir du map/filter/reduce.

value adults = [ for(p in people) if (p.age>=18) p ]

L’expressivité du langage

Bref, Ceylon est une version moderne de Java depuis ses fondations !

Voilà pour ce qu'est Ceylon. Revenons un peu sur le typage statique et implicite.

Les attributs (variables immutables) sont définis avec le mot clef value ou avec un type. On peut écrire tout un algorithme en n'utilisant que des values et le compilateur fait un maximum d'inférences de type. Notre code perd alors toutes ces déclarations de types qui alourdissent le code tout en gardant les erreurs de compilation lorsque l'on se trompe. C'est un typage implicite.

Ensuite, les API retournent des types que l'on ne verra pas dans le code mais qui apparaissent en infobulle lorsque l'on survole chaque mot clef "value". Ainsi, le compilateur vérifie tous les types même implicites. C'est donc un typage statique.

Enfin, ils ont poussé la logique du statique et implicite assez loin et c'est fort de café.

Ainsi, lorsqu'une liste est initialisée avec des constantes en chaine de caractères et en nombre entiers, le type de la liste est l'union des types : "String|Integer". Une "value" ayant un type uni est une instance d'un seul de ces types. On ne peut alors utiliser que les méthodes communes à tous les types de l'union. L'autre option est d'itérer sur cette liste composée de plusieurs types et/ou de faire un "switch case" sur ces unions de types :

void printDouble(String|Integer|Float val) {
  String|Integer|Float double;
  switch (val)
    case (is String) { double = val+val; }
    case (is Integer) { double = 2*val; }
    case (is Float) { double = 2.0*val; }
    print("double ``val`` is ``double``");
}

Le principe inverse existe avec des intersections de types où chaque instance est à la fois d'un type et d'un autre. On peut alors utiliser les méthodes des deux types. Je n'ai pas vu comment se passe cet héritage multiple dans le détail.

J'ai eu une surprise en voyant que Object est un type père de tous, mais fils de Anything et que ce dernier est père de Object et de Null. Null n'ayant qu'une seule instance en mémoire (null). Ainsi, les méthodes pouvant retourner une valeur nulle retournent leur type de valeur naturel ou Null. Cela s'écrit évidement "String|Null" ou en raccourci String? :

String? name = process.arguments[0];
if (exists name) {
  String notNullable = name;
  print("hello " + notNullable);
}

Cela signifie qu'une valeur pouvant être nulle (String?) et une valeur ne pouvant pas être nulle (String) n'ont pas strictement le même type.

J'ai eu deux dernières bonnes surprises. D'une part, les listes vides ont aussi leur type comme les valeurs nulles, les iterateurs finissent leurs itérations avec le type "Finished", ...

{String*} noStrings = {}; // {String*} est un iterable pouvant être vide et retourner Finsihed tout de suite
{String+} twoStrings = {"hello", "world"}; // two Strings ne peut pas être un itérable vide

D'autre part, des assertions permettent de faire le ménage. Comme le compilateur ne peut pas toujours être sûr de tous les types, il prends les assertions et autres casts comme des preuves. Par exemple, dans la cas précédent, le "name" est déclaré avec un point d’interrogation et cela signifie qu'il peut être nul. L'instruction "exists" qui suit teste que "name" n'est pas nul. Le système de typage de Ceylon est alors sûr que "name" ne peut pas être "nul" et la déclaration de "notNullable" sans le point d’interrogation est valide.

La portée des cas d’usages

Je me servirai de ce langage pour écrire rapidement des outils évolués mais internes à notre équipe comme des outils de reporting qui remontent quelques info provenant des logs mis sous ElasticSearch, des états des derniers build Jenkins, le bon déroulement de batchs de déploiement, etc... bref, ce que l'on appelle parfois les outils ou les "toy projects" professionnels.

Le site annonce une version 1.0.0 qui est prête pour de la production. Autant ces gens sont sérieux et crédibles, autant je ne conseille à personne de lancer un projet "sensible" sur une technologie avec laquelle il n'a pas suffisamment d'expérience. C'est d'ailleurs l'intérêt des projets d'outillage. Ils ont non seulement une taille et un coût raisonnable mais, en plus, ils partent en production...

L’état actuel

Enfin, pour ce qui est de la conférence/formation, elle nous a fait faire un vaste tour de ce qu'est Ceylon, de son écosystème et de l'état d'avancement de chaque fonctionnalité.

En effet, le langage est en version 1.0 "No More Mr Nice Guy" mais pas son environnement. Les plugins de l'IDE Eclipse sont au point mais écrits en Java. Le système de build concurrent du Maven de Java et du Gradle de Groovy est en pleine ré-écriture. Cayla est un framework web prometteur côté serveur ET client qui manque de templates, de widget (une taglib, du jsf, etc...).

Et dans le futur

En conclusion, je vois dans Ceylon, un phare pouvant indiquer une direction aux développeurs de Java. J'y vois aussi un langage plus expressif et potentiellement plus facile à écrire pour les développeurs d'applications métiers.

Le langage est enfin là, il faut maintenant qu'il trouve son public, qu'on trouve des patterns et des bonnes pratiques (comme l'usage de méthodes lazy dans les opérations sur les collections). J’espère voir fleurir des ateliers orientés sur l'usage du langage que ce soit sous forme de Kata ou d'application démo comme le fameux "Pet Shop" de "JEE".

L'avenir fera ses choix en temps et en heure, mais si jamais Oracle voulait se concentrer sur la JVM et ses outils de contrôle (JMC, JProfiler, etc.) et abandonner Java, cela ne m’inquièterait plus, la relève est déjà là !

Plus de publications

Comments are closed.