Octo @ ScalaDays 2010
Les 15 et 16 avril, la première rencontre de la communauté Scala s'est déroulée à l'EPFL à Lausanne, ville d'origine de ce langage.
100 % compatible avec le Java, le Scala est un langage orienté objets et fonctionnel et qui tourne sur la JVM. C’est un langage plus expressif mais dans lequel les concepts de Java se retrouvent facilement.
J'ai eu la chance de pouvoir participer à cette conférence. Elle regroupait 155 personnes venant principalement d'Europe, mais aussi des États-Unis et même du Japon ! Il y avait à peu près autant d'universitaires que de personnes issues de l'industrie informatique.
Je vais présenter les conférences qui m'ont semblé les plus intéressantes. Par souci de lisibilité, j’ai classé celles-ci de la moins « geek » à la plus « geek ».
Session « boss »
Infiltrer Scala dans le monde de l'entreprise
Le conférencier est un employé de la société Opower. Ses supérieurs adhèrent, implicitement ou pas, à une maxime : "We are married to only what works", ce qui veut dire en substance "Nous sommes mariés uniquement à ce qui fonctionne bien". Tout changement déstabilise, il est donc très difficile d'intégrer des nouveautés dans une entreprise qui fonctionne. Comment intégrer Scala à une architecture existante ?
Tout d'abord qu'est ce que ce langage peut apporter à une entreprise ? Qu'est-ce qui motive un décideur ?
- Faire le même travail en moins de temps : En effet, Scala est un langage plus expressif, où l'on peut faire la même chose qu'en Java en moins de lignes. On est donc plus productif et théoriquement on introduit moins de bugs.
- Attirer des gens talentueux : C'est un langage relativement complexe et pour le moment seuls les gens motivés et intéressés sont attirés par celui-ci.
- Une certaine compatibilité avec l'existant : Scala est à 100 % compatible avec Java et inversement. On peut donc commencer par une petite partie d'une application tout en continuant à faire du Java. De plus, on peut tout à fait programmer à la "Java" en Scala et petit à petit intégrer de nouveaux concepts. En revanche, au premier abord la syntaxe est assez différente et les concepts non-objets peuvent être difficiles à comprendre.
Vous voulez infiltrer ce nouveau langage dans votre application, que faut-il faire pour que celui-ci soit bien accepté par vos collègues et donc finalement convaincre votre supérieur de son intérêt ?
Il faut limiter son impact sur l'existant. Si on prend une application "classique" 3-tiers où peut-on intégrer du Scala ? La partie métier ? Persistance ? Présentation ? Celles-ci sont trop impactantes pour les migrer en Scala directement. Mais il y a une autre partie parmi les applications qui peuvent bien s'y prêter : les tests.
En effet, les tests sont souvent très lourds en termes de code et Scala peut facilement les alléger. De plus, il est simple d’écrire un nouveau DSL (Domain Specific Language) et donc d’en créer un qui soit adapté à un métier spécifique-un DSL de tests d’interfaces web adapté à une application par exemple.
Autre avantage, on peut remplacer les composants un à un grâce à la compatibilité quasi-complète avec Java, et ainsi réutiliser les bibliothèques existantes. De plus, le déploiement et a gestion de la production sont les mêmes, il n'est donc pas nécessaire de reformer les équipes de production.
Sessions « geek & boss »
Les deux présentations de Martin Odersky
Martin Odersky, créateur de Scala, était bien évidemment présent et a fait deux présentations, celle d'ouverture et celle de fermeture.
Conférence d'ouverture : le passé et le présent de Scala
L'histoire de Scala débute en 2001 avec les premiers designs. En 2005 sort la version 2.0, dont le compilateur est lui-même écrit en Scala. En 2006, l'industrie commence à l'utiliser. En 2008, Twitter annonce le remplacement du système de messages par un développement « maison » fait en Scala (cf. ici). En 2009, les principaux IDE (Eclipse,NetBeans, IntelliJ) ont tous des plugins pour l'utilisation du langage. Enfin, en 2010, les premiers ScalaDays sont organisés !
Quelques jours avant la conférence, la première Release Candidate de la version 2.8 tant attendue est arrivée. Celle-ci apporte une consolidation et des nouveautés :
- Paramètres nommés et par défaut
- Complet support des annotations Java, y compris les annotations nested. On pourra enfin faire du JPA en Scala
- Ajout de l'annotation @specialized, qui permet de générer automatiquement une version spécialisée d'une classe générique : ceci apporte un gain de performance non-négligeable et permet pour la première fois à Scala d'être plus rapide que Java sur les génériques
- Une nouvelle API complète sur les collections
- Les package objects
Je ne détaillerai pas ces nouveautés, car il faudrait leur consacrer un article complet. Si vous voulez plus d'information rendez-vous sur le site.
Des exemples d'utilisation du langage ont également été présentés par des industriels tels que Twitter, Linkedin, Xerox, Sony, Ebay, Novell, EDF Trading, Siemens, SAP, etc. On notera qu'EDF Trading finance depuis 2008 le développement du plug-in Eclipse.
Conférence de fermeture : le futur de Scala
Une nouvelle organisation du projet a été présentée. L'université EPFL n'étant plus en mesure de gérer à elle seule l'évolution de Scala :
- L'EPFL gardera son rôle d'antenne de recherche sur le projet et continuera de faire partie de la "core team".
- La Scala Fondation traitera tout ce que l'EPFL ne pourra pas gérer, c'est-à-dire superviser les développements Open Source, financer des développements auxiliaires, etc.
- Un support pour les entreprises qui rassurera les utilisateurs sur la pérennité du langage, assurera du support sur les versions anciennes et courantes de celui-ci, procurera du consulting et de la formation, et construira un réseau de consultants experts reconnus dans le monde de l'entreprise.
Il y a aussi une volonté de mieux prendre en compte les développements de la communauté dans ceux du langage, et ce grâce à la création d'une forge avec 3 niveaux :
- Un incubateur (incubator) pour les projets émergents
- Une "serre" (greenhouse) pour les projets intéressants de l'incubateur, mais pas assez matures pour être intégré à la branche principale
- Une branche principale (trunk) qui fournira les versions stables et qui intégrera les travaux considérés comme aboutis de la "serre"
Nous avons pu avoir en quelque sorte en avant-première la roadmap totalement instable des futurs développements. Celle-ci s’adresse plus à des développeurs Scala confirmés.
Vers la fin 2010, la version 2.9 devrait apporter les collections parallèles, plus de spécialisation, des Vectors et Hashs plus rapides ainsi que peut-être un type Dynamic (grâce à l'ajout de invokedynamics dans Java 7, cf. cet article), une API de réflexion qui prenne en compte les spécificités du langage, et une API de I/O.
Il est possible qu'en 2011 sorte une version 2.10 comportant de la programmation réactive, du optionnal exception checking (être capable de ne pas traiter des exceptions dans un objet mais dans un autre, utile pour les collections), les premiers DSL parallèles, Scala sur la plateforme .Net (qui est financé par Microsoft).
Enfin, pour une date indéterminée comprise entre 2012 et 2015 une version 2.11 ou peut être 3.0 pourrait apporter la programmation par contrats, des informations plus précises sur les messages d'erreurs du compilateur lors de la résolution des types, une optimisation des DSL parallèles, etc.
Scala @ Linkedin
Le fameux site réseau social orienté entreprise, Linkedin, a des problématiques peu communes. Par exemple, ils reçoivent en moyenne 15 millions de recherches par jour sur leur site, avec des pics à 250 recherches par seconde.
Pour palier à cette charge, ils distribuent les recherches sur plusieurs serveurs qui ont une connaissance partielle puis ils effectuent un merge avant de servir le résultat de la recherche.
Ils ont donc des besoins en clustering, partitionning et répartition de charge. Ils se sont donc tournés vers les outils ZooKeeper, Netty, Protocol Buffer. Et ils ont développé Norber, une bibliothèque qui permet de gérer des clusters et qui fournit une API client/serveur qui sait prendre en compte des clusters. Ainsi, Norber se base sur les outils précédemment cités.
Les principales raisons pour lesquels Linkedin a décidé d'utiliser Scala sont : son API d'acteurs, les possibilités de réutilisation de code avec les traits.
Akka
Akka est une bibliothèque d'acteurs alternative à celle de l'API standard de Scala. D'ailleurs, elle se veut 4 fois plus rapide que celle de Scala. Elle possède deux modèles d'acteurs :
- Par thread
- Acteurs avec mémoire partagée par un lock
- Par évènements
- Taille de 600K en mémoire
- 6.5 millions d'acteurs sur 4 go de RAM
Son utilisation reste tout à fait accessible : pour créer un acteur il suffit d'étendre la classe Actor et d'implémenter la méthode receive ; pour envoyer un message de façon asynchrone on utilise la méthode ! ; pour envoyer un message de façon synchrone c'est la méthode !!.
Akka est aussi capable de gérer des acteurs distants, de deux types :
- initialisé par le client et géré par le client
- initialisé par le serveur et géré par le serveur
Cette bibliothèque possède aussi une API de collections transactionnelles (STM) qui est capable de gérer des rollbacks complet.
Sessions « geek »
Les collections parallèles
Cette conférence traitait de la manière d'adapter les collections à la programmation multi-cœur, par exemple la parallélisation d'une opération de filtre sur une liste. Toute cette API hérite des interfaces et traits de l'API des collections de Scala 2.8. Elle sera d'ailleurs intégrée dans Scala 2.8.1.
Une implémentation naïve des collections parallèles consisterait à découper la collection et à faire traiter chaque sous-collection par un cœur, puis de remerger les collections résultantes. Cette méthode ne fonctionne pas si on utilise des collections classiques (ex : opération de tri sur une liste). Il faut donc d'abord les réimplémenter en trouvant des structures de données qui se prêtent bien aux split et merge.
Il faut donc une méthode split pour découper une collection et une méthode merge/combine pour recréer la collection finale. Il y a plusieurs façons d'implémenter un split :
- copier les éléments de la collection
- produire un wrapper pour les encapsuler
De même pour la méthode combine :
- copie des éléments
- évaluation paresseuse (lazy)
Il y a plusieurs méthodes de découpage possibles, mais il faut en trouver une qui puisse gérer une répartition des calculs "intelligente". L'idée est de découper la collection en un grand nombre de petites sous-collections pour répartir le travail entre chaque processeur plus finement. Si un cœur est en "retard" par rapport à un autre ça ne pénalisera pas trop la tâche complète, puisque un autre cœur en "avance" pourra continuer de traiter d'autres sous-collections. Mais plus on divise la collection plus l'overhead du split et du merge sera important. Le principe utilisé est le suivant : on découpe la collection en autant de sous-ensembles que de cœurs disponibles. Puis on découpe chaque sous-ensemble en 2, un seul de ses deux sous-sous-ensembles est à son tour découpé en deux, et ainsi de suite jusqu'à un certain seuil qui est en fonction de la taille de la collection. Puis on "affecte" chaque sous-ensemble à un cœur.
Attention, ces collections ne gèrent pas la concurrence mais uniquement le calcul parallélisé. De même, cette API n'est pas efficace pour de petites collections puisque l'overhead du split/merge est trop important. Malgré tout, celle-ci est aussi efficace en termes de temps de traitement que la librairie Java équivalente Extra166.
Les arguments nommés et par défaut
Cette conférence traitait d'une des nouveautés de Scala 2.8, les arguments nommés et par défaut, leur utilisation et leur implémentation. Les arguments nommés permettent d'appeler une méthode en spécifiant le nom de chaque paramètre par exemple :
def connect(host = "localhost", port = 3999, login = "root", password = "secret") { ... }
connect() // équivalent à connect("localhost", 3999, "root", "secret")
connect("my.host") //équivalent à connect("my.host", 3999, "root", "secret")
connect(host = "my.host") //équivalent à connect("my.host", 3999, "root", "secret")
connect("my.host", port = 22) //équivalent à connect("my.host", 22, "root", "secret")
Cette fonctionnalité évite clairement la duplication de code. Les paramètres par défaut peuvent être n'importe quel type d'expression, ils peuvent même se référer à des arguments précédents, et fonctionnent même avec des types génériques :
def connect(user: User, name = user.name) { ... }//référence à un argument précédent
def connect[T](name:T = "my name") { ... }
Une méthode, qui en surcharge une avec des arguments par défauts, peut redéfinir ceux-ci.
Il faut faire attention à deux points lors de l'utilisation des arguments nommés : - Leur nom : il est par exemple déconseillé d'inverser le nom de deux paramètres pour une méthode surchargée - Leur syntaxe est la même que celle de l'assignation, il peut donc y avoir des ambiguïtés :
def id(x: Int) = x
var x: Int = 1
id(x = 2) // pas d'ambiguïté car x = 2 => Unit, et Unit ó void
def id[T](x: T) = x
var x: Int = 1
id(x = 2) // compilation error
C'est une fonctionnalité qui a été implémentée au niveau des sources et non du ByteCode. En fait, l'appel d'une méthode avec des arguments nommés ou par défaut se transforme en un appel classique mais qui prend en paramètre des variables générées automatiquement contenant les paramètres d'appel :
def connect(host = "localhost", port = 3999, login = "root) { ... }
connect(login = "directoryManager")
//Qui est généré en :
val connect$default$1 = "localhost"
val connect$default$2 = 3999
val connect$default$3 = "directoryManager"
connect(connect$default$1, connect$default$2, connect$default$3)
SBT : Simple Build Tool
SBT est un outil de build écrit en Scala destiné à construire des projets en Scala, utilisant Ivy pour la gestion des dépendances.
Il possède plusieurs avantages par rapport aux outils existant (tel que Maven) ; il exécute toute ses opérations dans la même JVM et ne donc subit pas l'overhead dû au lancement de celle-ci à chaque opération. Du reste, la plupart des actions se font dans un shell interactif où l'on peut appeler les différentes targets du projet. Autre fonctionnalité intéressante : il détecte automatiquement la modification d'un fichier source et relance la compilation.
Le format de description d'un projet est écrit directement en Scala grâce à un petit DSL (qui gère aussi les dépendances) ; le fichier de configuration bénéficie aussi de la recompilation automatique. Grâce au DSL, La syntaxe du fichier de configuration est très claire et se comprend très bien, même si on ne connaît pas l'API de SBT.
SBT possède plusieurs plug-ins intéressants, qui sont livrés sous format de code source. En effet, le développeur trouve qu'il est plus simple de garantir une compatibilité au niveau des sources qu'au niveau des binaires ; de plus toutes les structures sont immutables ce qui évite les effets de bord désastreux. Notons qu'un des plug-ins permet d'avoir une console Scala avec tout l'environnement d'un projet pré-chargé, les railers reconnaîtront la console de rails.
Par exemple, si on veut définir une nouvelle tâche avec une dépendance pour notre projet :
lazy val hello = printTask("toto") dependsOn otherTask
Il est aussi possible de définir des paths très simplement :
val path = "src" ** ("*.scala" | "*.java") // touts les fichiers .scala et .java sous le répertoire src
Conclusion
Cette conférence était remarquablement bien organisée, félicitations aux organisateurs ! Ces deux journées ont prouvé tant par leur contenu que par la représentativité de haut niveau de ses participants, que le Scala est capable de devenir un langage de premier ordre. En effet, dès ses débuts, il a su s'adapter tant aux contraintes de l'entreprise qu'à celles du monde universitaire.
Je terminerai sur une traduction libre d'une phrase d'un participant : "Ça me fait penser au premier JavaOne, pleins de gens intéressés et intéressants."