Notre offre Aerospace Notre offre IT
Conseil métier Airline, offshore IT et dissémination sur les projets européens... Conseil, formation et projets dans les Technologies de l'information, tous secteurs d'activités...
 
Blog > Article
 
 

Scala et avenir de Java

Depuis son apparition en mai 1995, juste avant l’explosion d’Internet, Java a beaucoup évolué. Il est notamment devenu une référence en matière de développement d’applications professionnelles, Web comme Desktop. Après ces 14 ans il convient de dresser un bilan sur l’adéquation de Java aux objectifs et contraintes actuelles.

On constate deux approches distinctes :

  • Beaucoup se focalisent sur Java 7 : les discussions font rage sur les évolutions qui pourraient composer cette nouvelle version
  • D’autres considèrent que Java, avec sa spécification de 600 pages, est devenu trop complexe : qu’il ne pourrait donc plus apporter de réelle révolution et qu’il faudrait donc réfléchir à son digne successeur.

Complexité ?

Nous sommes bien d’accord, cette notion de complexité est relative : par exemple, tout le monde ne se sent pas obligé de mettre en œuvre et pousser les Generics jusqu’à leurs retranchements !

Tout le monde n’a pas été convaincu par la pertinence des nombreux débats  « langages statiques contre langages dynamiques ». Ces débats ont malgré tout ouvert la voie à de nouveaux paradigmes de développement rapide et flexible.

Certains se sont donc consacrés à la création d’un langage « état de l’art » afin de répondre aux problématiques actuelles avec plus de simplicité et de productivité, tout en conservant les principes qui ont fait le succès de Java. Finalement, comme Java, qui avait en quelques sortes détrôné le roi de l’époque, C++.

Cet article est consacré à Scala, l’un de ces langages. Scala a été démarré en 2001 par Martin Odersky, Professeur à l’EPFL qui travaille également pour Sun à la production de l’implémentation de référence de javac. C’est lui qui est à l’origine de l’implémentation des Generics Java.

Je dois avouer que je fais partie des nombreuses personnes qui ont été impressionnées par ce langage dès le premier contact. Par exemple, James Strachan, le créateur du langage dynamique Groovy, a lui aussi été séduit par Scala :

“Though my tip though for the long term replacement of javac is Scala. I’m very impressed with it! I can honestly say if someone had shown me the Programming in Scala book by Martin Odersky, Lex Spoon & Bill Venners back in 2003 I’d probably have never created Groovy”

Et InfoQ nous présente d’autres « mordus » de Scala.

Langage statique ou langage dynamique ?

On peut lire beaucoup de débats passionnés sur ce sujet. Mais pour le profane, il n’est pas facile d’y participer car la terminologie associée n’est pas si triviale. Commençons donc par quelques éclaircissements.

Qu’est-ce qu’un langage statique ?

Déjà, il faut savoir que « langage statique » est un abus de langage pour « langage à typage statique ».

J’aime assez la définition suivante, proposée dans le livre Programming Scala de Dean Wampler et Alex Payne (O’Reilly) :

“in static typing, a variable is bound to a particular type for its lifetime. Its type can’t be changed and it can only reference type-compatible instances”

Ainsi, une variable de type A ne pourra être réaffectée plus tard qu’à des types compatibles avec le type A (dans un langage orienté objets, les types compatibles avec un type A sont A lui-même et les sous-classes de A). En fait, la notion de type est rattachée aux variables, non aux valeurs.

On pourrait compléter cette définition informelle, mais dans notre contexte elle nous suffira amplement.

Java et C++ sont des exemples de langages statiques.

Et un langage dynamique ?

Le même livre nous donne la définition suivante du typage dynamique :

“In dynamic typing, the type is bound to the value, not the variable”

Ainsi, une variable qui référence une valeur de type A peut très bien être réaffectée plus tard à des valeurs de n’importe quel type, même non compatible avec le type A. Dans ce cas, la notion de type est rattachée aux valeurs, non aux variables.

C’est pour cette raison que l’on parle de typage dynamique : le type des variables est évalué lors de l’exécution de l’application, et non lors de la compilation comme c’est le cas du typage statique.

Ruby, Python, Groovy et JavaScript sont des exemples de langages dynamiques.

Le débat

Peut-être qu’en lisant ces définitions, compte tenu de votre expérience personnelle, vous tendez naturellement vers une de ces deux approches.

Ainsi on entend souvent les arguments suivants :

  • « Les langages dynamiques, plus flexibles, permettent un gain de temps sur le choix des types» 
  • « Les langages statiques peuvent davantage exploiter la notion de type, ce qui facilite la création d’outils d’aide au développement comme l’auto-complétion et d’outils d’optimisation du code» 
  • « Les langages dynamiques retardent la détection de certaines erreurs à l’exécution alors que les langages statiques permettent de lever ces erreurs dès la compilation» 
  • « Une batterie de tests unitaires permet aux langages dynamiques de s’affranchir de la détection d’erreurs par un compilateur» 

Et bien d’autres… Ces simples exemples suffisent à vous faire imaginer l’intensité des polémiques, dans un sens comme dans l’autre ! Sans rentrer dans les détails de ces exemples, essayons de dépasser le débat.

Un peu de recul…

On constate deux faits intéressants :

  • Premier constat, les langages dynamiques séduisent beaucoup de monde de par leur flexibilité et leur souplesse : le succès des langages Ruby et Python, pour ne citer qu’eux, en témoigne. Les aficionados de ces langages trouvent souvent les langages statiques trop verbeux et ils apprécient d’avoir moins de code à écrire
  • Second constat, beaucoup ne sont pas séduits par ces langages… Car cette flexibilité et cette souplesse font peur ! Après tout, écrire moins de lignes de code n’augmente pas nécessairement la productivité : il faut également que ce code soit fonctionnel, lisible, maintenable, etc.

Il est clair qu’il y a une réelle demande de langages succincts et flexibles pour gagner en productivité. Mais diminuer la quantité de code n’est pas un objectif en soi : il ne s’agit pas simplement d’une histoire de trop grande verbosité ou taille du code, mais de profiter davantage de l’inférence de type, c’est-à-dire éviter d’écrire ce que le compilateur peut deviner.

Les grandes lignes de Scala

Un langage statique

Scala est un langage statique qui permet une syntaxe succincte, flexible et même élégante. Pourtant son système de typage est sophistiqué.

Pour quelles raisons ? J’aime beaucoup la façon dont le débat habituel est dépassé dans le livre Programming in Scala, qu’il a co-écrit avec Lex Spoon et Bill Venners (Artima). Il nous rappelle en effet que :

  • Oui, un compilateur ne permet de détecter que de simples erreurs : il ne vous dira pas que certaines fonctions ne se terminent pas ou que votre cahier des charges n’est pas respecté
  • Oui, des tests unitaires permettent d’avoir une couverture plus poussée

Mais il nous invite à envisager le débat sous un autre angle :

  • Un système de typage statique ne peut pas remplacer les tests unitaires, mais il peut en réduire le nombre
  • L’inverse n’est pas vrai : les tests unitaires ne peuvent remplacer un système de typage statique. En effet, des tests ne peuvent que nous prouver la présence d’erreurs et non leur absence. Alors qu’un typage statique nous donne des garanties simples mais réelles (opérations entre types incompatibles, accès externe à des membres privés, etc.)
  • Enfin, le typage statique facilite les refactorings de code et la mise à jour des documentations.

Un langage  concis

Scala est donc un langage statique. Pourtant, James Strachan nous explique – à juste titre – que Scala est un langage dont la concision rappelle les langages dynamiques :

“Scala seems to hit the perfect sweet spot between the consise feel of a dynamic language, while actually being completely statically typed”

En fait, Scala repose sur de nombreuses techniques permettant de minimiser la syntaxe inutile : il s’agit essentiellement de l’inférence de type que nous évoquions précédemment. Ces techniques dépassent largement le cadre de cet article, nous nous contenterons d’un exemple très simple.

La ligne suivante de code Java :

List<String> list = new ArrayList<String>();

Peut être écrite en Scala :

val list : List[String] = new List[String]

Ou même :

val list = new List[String]

Cette dernière ligne de code semble provenir de langages dynamiques mais ici le typage est statique : il est inutile de préciser le type de la variable car le compilateur est capable de le déterminer seul. C’est l’inférence de type.

L’introduction à Scala sur le site officiel évoque ainsi une diminution du volume du code d’un facteur 2 à 3.

Encore une fois, le but n’est pas de produire moins d’effort de typage, mais de produire moins d’effort de lecture et de compréhension donc de réduire le nombre d’erreurs possibles. L’exemple suivant est simple mais il illustre cette idée.

Voici un code classique de création d’une classe en Java :

class Point {
   private int x;
   private int y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

Voici l’équivalent Scala :

class Point(x : Int, y : Int)

Note : en toute rigueur, dans ce code Scala, les variables d’instances seront final. Ceci dit, l’idée de cet exemple est simplement de vous faire prendre conscience de la concision de Scala.

En outre, Scala propose :

  • L’utilisation de caractères non alphanumériques dans les noms des méthodes : cela permet un équivalent de la surcharge d’opérateurs
  • Les closures, qui manquent souvent aux développeurs Java qui connaissent Ruby et Python.

Voici un exemple illustrant la notion de closure et son intérêt. Voici une simple méthode Java qui retourne la liste des items achetés par un utilisateur :

public List<Item> getBoughtItems(User user) {
   List<Item> result = new ArrayList();
   for (Item item : items) {
      if (user.bought(item)) {
         result.add(item);
      }
   }
   return result;
}

Voici l’équivalent Scala, basé sur une closure :

def getBoughtItems(user: User) =
   items.filter(user bought)

En fait, l’expression user bought désigne un prédicat. Ce prédicat prend un paramètre en entrée, qui est omis car unique.

Un langage mixte

Martin Odersky connaissant bien la Programmation Orientée Objets et la Programmation Fonctionnelle, a créé Scala comme une synthèse du meilleur de ces deux mondes :

  • La Programmation Orientée Objets permet la construction de systèmes complexes, structurés et flexibles
  • La Programmation Fonctionnelle permet de répondre rapidement et de manière concise à certaines problématiques.

Scala est un donc un langage dit « mixte ». Et c’est ce mélange, avec ses nouveaux design patterns et son style concis, qui le rend « scalable ». Car oui, le nom « Scala » est issu de la contraction de « scalable language ».

Un langage « scalable » ?

Je vous prie de m’excuser car j’emploierai parfois le terme anglais « scalable » au lieu de son équivalent français « évolutif », car cela rappelle bien les objectifs de ce langage.

Ce langage se veut évolutif car conçu pour évoluer avec la demande des utilisateurs. Comme nous l’a présenté Martin Odersky à la conférence JavaOne 2008 :

« A language is scalable if it is suitable for very small as well as very large programs» 

Le livre Programming in Scala nous explique la démarche derrière Scala : il ne s’agit pas de fournir le langage parfait qui contient « tout» , mais de donner des outils que l’on peut étendre et adapter.

Un langage pour la machine virtuelle Java

Scala s’inspire d’un nombre impressionnant de langages (il suffit de lire la page 20 du livre précédent pour s’en rendre compte).

Mais le compilateur Scala produit du bytecode pour la JVM, ou machine virtuelle Java. En effet, durant ces 14 ans :

  • Beaucoup d’optimisations ont été apportées à la JVM : il est donc intéressant de profiter des performances obtenues d’entrée de jeu
  • Les développeurs ont acquis beaucoup de compétences et produit de nombreux frameworks et applications : il serait donc dommage de ne pas profiter de toute cette richesse.

Il est ainsi possible d’invoquer du code Java depuis du code Scala, et inversement.

Un langage orienté objets

Un langage orienté objets à 100%

Scala est un langage orienté objets à 100%, construit autour du slogan « everything is an object» .

En effet, chaque valeur est un objet et toute opération est un appel de méthode. Ainsi, Scala élimine les déviations de Java :

  • Il n’y a pas de type primitif
  • Il n’y a pas de membre statique (qui rappelons-le, ne sont par définition rattachés à aucune instance) : des singletons sont utilisés à la place
  • Les tableaux ne sont pas gérés via une syntaxe spéciale : il existe une classe Array comme les autres.

Ces déviations ne paraissent pas si gênantes, mais elles compliquent les choses et limitent la « scalabilité » du code.

Un système de type sophistiqué

Scala permet la définition des types classiques : classes, héritage, types abstraits… Mais son système de type est sophistiqué :

  • Il étend celui de Java (même s’il repose beaucoup dessus en interne)
  • Les Generics sont plus flexibles
  • Plus de constructions sont possibles grâce à l’inférence de type

On notera en particulier les deux apports suivants :

  • La notion de traits : les traits ressemblent aux interfaces Java, sauf qu’on peut en fait y définir des propriétés et y implémenter certaines méthodes. Les traits apportent de nouveaux design patterns que nous pourrons aborder dans un autre article
  • La notion de composition mixin, qui permet de réutiliser des membres spécifiques à une classe fille (par rapport à sa classe mère) lors de la définition d’une nouvelle classe : cette notion, que vous pouvez rapidement découvrir sur le site officiel, permet ainsi d’ajouter de l’état et du comportement à des classes (ou même des objets), ce qui était possible en C++ par de l’héritage multiple mais que partiellement en Java via des approches de type AOP (Programmation Orientée Aspects).

Voici un exemple simple, proposé sur le site officiel :

abstract class AbsIterator {
   type T
   def hasNext : Boolean
   def next : T
}

// Ici le mot-clé « trait » indique que cette
// classe pourra être utilisée comme mixin
trait RichIterator extends AbsIterator {
   def foreach(f : T => Unit) {
      while (hasNext) f(next)
   }
}

class StringIterator(s: String) extends AbsIterator {
   type T = Char
   private var i = 0
   def hasNext = i < s.length()
   def next = { val ch = s charAt i; i += 1; ch }
}

// Combinons les fonctionnalités des deux classes par
// composition mixin
object StringIteratorTest {
   def main(args: Array[String]) {
      class Iter extends StringIterator(args(0))
         with RichIterator
      val iter = new Iter
      iter foreach println
   }
}

Dans ce cas, on dit que la classe Iter est construite par composition mixin des classes StringIterator et RichIterator. La première est son « parent» , la seconde son « mixin» .

En fait, cela permet à Scala de proposer une nouvelle approche orientée modules : les composants sont des classes ou des traits, et on peut les composer par mixin.

Et… Un langage fonctionnel !

Scala est également un langage de programmation fonctionnelle.

Une petite présentation de la programmation fonctionnelle

Pour la petite histoire, ce type de langages est plus ancien que les langages orientés objets, mais ils étaient jusqu’ici cantonnés à des laboratoires de recherche. Si vous souhaitez en savoir plus sur l’histoire de ces langages, consultez par exemple le livre Programming in Scala.

Dans ce type de langages, les fonctions sont des valeurs de premier ordre (comme les int, string, etc. dans nos langages habituels) : on peut assigner des fonctions à des variables, passer des fonctions en arguments d’autres fonctions, déclarer des fonctions comme valeur de retour d’autres fonctions, etc. Autrement dit, toujours dans la lignée du « everything is an object », en Scala les fonctions sont des objets comme les autres. A titre d’illustration, cela va plus loin que les pointeurs de fonctions du C++ (qui ne sont que des valeurs de second ordre). Nous pourrions là-encore pousser le sujet bien plus loin, mais restons dans le cadre de cet article.

L’immutabilité comme principe de base

Les fonctions prennent en entrée des valeurs qu’elles ne modifient pas : ces données sont dites « immutables ». Les fonctions sont « sans effet de bord », ou « référentiellement transparentes » : dans un fragment de code, on peut remplacer un appel à une fonction par son résultat, c’est-à-dire sa valeur de retour.

Ce principe est fondamental et il peut faire peur puisque la programmation orientée objets ne le respecte pas nécessairement. En fait, cette partie de Scala s’intègre parfaitement avec la partie orientée objets : elles ne s’opposent pas, elles s’améliorent mutuellement.

Quel en est l’intérêt ?

Ce type de langages permet de simplifier plusieurs problèmes de design. Un bon exemple en est la programmation concurrente.

Ce sujet est de plus en plus d’actualité avec la prolifération des processeurs multi-core, qui distribuent les calculs sur différents threads. Or il s’agit souvent d’un vrai casse-tête !

Le modèle de Java en matière de programmation concurrente repose sur l’accès synchronisé à des ressources mutables et partagées : il est donc complexe et source d’erreurs. La gestion des locks est difficile, surtout pour des systèmes qui grandissent en taille et en complexité. En effet, comme nous le rappelle judicieusement le livre Programming in Scala, les deadlocks ne se voient souvent qu’une fois en production et non pendant les tests.

Scala apporte une solution élégante à ce problème : l’API Actors, qui repose sur le modèle du même nom, issu du langage Erlang. Elle repose sur les principes suivants :

  • On appelle « acteurs » des entités logicielles indépendantes
  • Les acteurs ne partagent pas d’information d’état entre eux, ils communiquent en échangeant des messages asynchrones (chacun dispose d’une queue) : on n’a donc pas besoin de synchroniser l’accès à des données mutables partagées.

Sur ce sujet, je vous invite à étudier cet exemple du livre Programming Scala, ainsi que la page suivante du site officiel.

Les closures, que nous avons présentées précédemment, proviennent directement de la programmation fonctionnelle.

Je vous invite également à jeter un œil au Pattern Matching, sorte de switch/case avancé. Là encore nous pourrons aborder ce point spécifique dans un article ultérieur, car cela va bien plus loin qu’un simple switch/case !

Scala en pratique

Nous venons de présenter Scala, sur le papier. Qu’en est-il en pratique ?

Scala est déjà mis en œuvre dans des projets en production, comme nous l’indique le site officiel. On y découvre que Twitter, Siemens et même une filiale d’EDF (EDF Trading) ont mis en œuvre Scala de manière plus que significative afin d’améliorer la productivité, la « scalabilité»  et la fiabilité générale de projets concrets.

Côté performances, le site officiel se veut catégorique :

“The Scala compiler is mature and proven highly reliable by years of use in production environments, The compiler was written by Martin Odersky who also wrote the Java reference compiler and co-authored the generics, used by millions of Java programmers today. You can be confident his implementation of the Scala compiler produces byte code that performs every bit as good as comparable Java code”

Côté outils, il existe déjà des plugins pour les outils de développement traditionnels (Eclipse, IntelliJ IDEA, NetBeans) et pour les outils de build (Ant, Maven).

Notez enfin que Scala est même disponible pour .NET et sa machine virtuelle (CLR).

Enfin, la communauté autour de Scala est déjà organisée : la distribution de Scala est téléchargée 5000 fois par mois, et des frameworks Scala voient déjà le jour. On notera par exemple le framework Web nommé Lift. Une conférence dédiée a même déjà été lancée : http://www.scalaliftoff.com/.

Conclusion (temporaire)

J’espère vous avoir transmis mon intérêt pour Scala ! Scala n’est pas une amélioration de Java : il s’agit d’un langage qui se veut plus simple et plus élégant que Java, tout en produisant du bytecode pour la machine virtuelle Java. Scala pourrait donc bien être un digne successeur de Java. Mais il ne faut pas pour autant jeter aux orties les autres langages comme Ruby, Groovy ou Python : n’oublions pas que chaque situation a ses besoins et ses contraintes, et donc ses solutions. A suivre…

3 commentaires

Bonjour,

Quel plaisir de trouver un tel article sur Scala en Francais !
J’en profite pour faire un peu de pub pour la librarie de BDD que j’ai creee: http://code.google.com/p/specs/.

N’hesitez-pas a l’utiliser a la place de JUnit pour profiter d’une syntaxe bien plus flexible que JUnit pour ecrire des tests unitaires !

Eric.

Merci pour cet article vraiment clair et intéressant.
Si Scala permet de coder tout ce que permet Java, pourquoi Scala ne deviendrait-il pas tout simplement la prochaine syntaxe officielle de java ?

Je suis aller voir le site officiel et les exemples.
Beaucoup de bruit pour rien et cela n’ammène que de la confusion.
En fait en programmation avec de l’expérience tu te fabriques ton framework Objet en Java qui structure tes développements. C’est de cela que vient l’efficacité de ton travail, la qualité de tes bibliothèques objets. Tu peux très rapidement encapsuler certaine syntaxe répétitive dans une class etc…. et obtenir quelque chose de simple.
Amusant pour les débutants, inutile et contre productif pour les expérimentés.
De toute manière, il y’en a toujours qui se croit plus intelligent que tout le monde. Bref c’est à la limite du Buzz ce Scala.

Laissez un commentaire