Scala Native : accéder au bas niveau depuis Scala

Le langage Scala n’est pas dénué d’avantages, comme beaucoup d’autres : il est typé (comme C ou Pascal), mais ne force pas à indiquer les types explicitement dans le code (contrairement aux précités, mais comme Python) ; il n’impose pas une verbosité monstre (contrairement à Java), avec une syntaxe orientée productivité (comme Python) ; il est raisonnablement rapide (comme Java, mais nettement moins que C), mais permet d’exploiter le parallélisme très facilement (contrairement à Python).

Par contre, un des inconvénients de Scala est sa vitesse d’exécution, pas toujours suffisante : il faut compter le temps de démarrage de la JVM, puis les optimisations que le moteur JIT peut effectuer sur le code qui lui est soumis, sans oublier que le ramasse-miettes peut venir jouer. La sécurité que le langage offre peut aussi aller à l’encontre de la performance à l’exécution : il est impossible de s’échapper du bac à sable défini, à l’exception de quelques utilisateurs très expérimentés. Finalement, au niveau de l’accès aux bibliothèques natives préexistantes, leur réutilisation est presque impossible — après tout, ces bibliothèques natives pourraient causer des problèmes de sécurité !

Pour résoudre ces problèmes, une équipe de l’EPFL, déjà à l’origine de Scala, a lancé le développement de Scala Native. Ce compilateur Scala exploite LLVM, l’infrastructure de compilateurs derrière Clang, notamment. La syntaxe de cette variante de Scala ne diffère de l’originale que par quelques ajouts, dont l’objectif est de faciliter l’interopérabilité avec des bibliothèques natives, comme des structures C (avec l’annotation @struct sur une classe) ou encore l’allocation de mémoire sur le tas avec des pointeurs :

@struct class Vec ( // Définition d'une structure en C.
  val x: Double
  val y: Double
}
val vec = stackalloc[Vec] // Équivalent à un malloc() en C ou un new en C++ : vec est un pointeur sur le tas.

Pour accéder directement à des fonctions C (comme malloc()), il est aussi possible d’utiliser l’annotation @externe et directement définir la fonction comme utilisable :

@extern object stdlib {
  def malloc(size: CSize): Ptr[_] = extern
}
val ptr = stdlib.malloc(32)

Au niveau de l’implémentation, le projet Scala Native ne duplique pas l’analyse syntaxique du compilateur, mais l’utilise (ou bien dotty, la prochaine génération du compilateur Scala). Il profite donc d’une base de code bien établie, ainsi que d’optimisations éprouvées dans le contexte du code Scala.

À l’exécution, bien sûr, Scala Native exploite toujours un ramasse-miettes, le classique Boehm, simplement parce qu’il a l’avantage de déjà exister. Les développeurs notent qu’il ne peut que s’améliorer par rapport à cette implémentation générique. La bibliothèque standard Java est partiellement disponible, toute la bibliothèque standard C est accessible.

Ce générateur de code natif depuis Scala n’est pas encore prêt pour le plus grand nombre, il a, à peine, été annoncé ce mois-ci. Ses objectifs ne concernent pas les temps de compilation. Cependant, il montre que le langage Scala veut s’exporter de plus en plus, après un autre générateur de code JavaScript pour utiliser son code dans des navigateurs Web.

Source (dont image) : présentation à Scala NYC.
Voir aussi : le site officiel de Scala Native, le projet sur GitHub.

Calendrier 2016 pour Scala : la version 2.12 en vue

Scala est un langage de programmation pour la JVM, c’est-à-dire qu’il exploite l’écosystème Java, mais avec une syntaxe et des concepts radicalement différents de ceux de Java, tout en maintenant une forte compatibilité (tout code Java peut être appelé depuis Scala et vice-versa). Scala est donc un langage orienté objet, mais fonctionnel depuis sa conception (contrairement aux extensions fonctionnelles de Java 8) ; il possède également un système de typage fort. De manière générale, il tend à être concis tout en éliminant les défauts du langage Java — sa conception a débuté en 2001 à l’EPFL, dans le laboratoire de méthodes de programmation (LAMP).

La série actuelle, numérotée 2.11, sera la dernière à être compatible avec Java 6, avec des versions régulièrement mises à disposition. Les développeurs peaufinent énormément cette version, car la suivante est l’objet de grands travaux, avec notamment l’élimination de la compatibilité avec Java 6 et 7. La dernière version devrait être la 2.11.9, au troisième trimestre de 2016, c’est-à-dire deux ans après la première version de Scala 2.11.

Le futur s’annonce de plus en plus excitant : pour tirer le meilleur parti de Java 8, le compilateur encodera d’une manière très différente les traits et fonctions anonymes, beaucoup plus proche du comportement actuel de Java 8. Ainsi, l’interopérabilité sera beaucoup plus facile avec Java, avec la possibilité d’écrire des fonctions anonymes en Java pour les utiliser avec une bibliothèque Scala et vice-versa — ceci, sans changement du code Scala. Du côté technique, cet encodage utilisera les pointeurs sur méthodes (instances de la classe MethodHandle), comme Java 8, ce qui devrait diminuer les temps de compilation et la taille des binaires générés, même si le gain de performance n’est pas clair.

De manière plus générale, le compilateur a droit à une cure de jouvence, basé sur les derniers travaux côté EPFL de Miguel Garcia (partiellement intégrés dans Scala 2.11) : de manière générale, seule l’analyse syntaxique du code Scala est gardée par rapport aux versions précédentes, le reste a été fondamentalement retravaillé pour augmenter la performance (la compilation est de quinze pour cent plus rapide qu’avec Scala 2.10, sur tout le processus) et la taille des binaires générés.

La syntaxe du langage n’évolue pas entre Scala 2.11 et 2.12, toutes ces améliorations se font sous le capot. Actuellement, la préversion 2.12 M3 est disponible depuis début octobre ; elle est déclarée suffisamment stable pour que les développeurs de bibliothèques commencent à tester et à distribuer des versions compilées de leurs bibliothèques. La prochaine, numérotée 2.12 M4, est prévue fin janvier 2016, avec la première version admissible 2.12 RC1 fin mai 2016. La version finale n’est pas encore planifiée, mais ne devrait pas tarder, à moins que des défauts majeurs soient trouvés.

Les versions suivantes devraient reprendre le train des améliorations dans la syntaxe et la bibliothèque standard, avec un compilateur bien plus facile à maintenir et à faire évoluer. Rien n’est encore véritablement planifié, mais les améliorations imaginées comportent :

  • une simplification des collections (par exemple, de leur hiérarchie), par défaut immuables, avec un déplacement de fonctionnalités en dehors de la bibliothèque standard, notamment pour faciliter l’optimisation ;
  • des collections parallèles avec des améliorations de performance, obtenues par la fusion d’opérations et un ordonnancement ;
  • l’intégration de la réflexion et des macros à la Lisp comme fonctionnalités pleinement supportées (certaines sont actuellement expérimentales) ;
  • une syntaxe simplifiée et unifiée, pour se rapprocher des principes fondateurs du langage : un ensemble réduit de fonctionnalités orthogonales faciles à assembler (comme Lisp) ; la syntaxe XML pourrait ainsi disparaître, tout comme les fonctionnalités qui mènent régulièrement à des comportements inexplicables pour les débutants.

Source : 2016 Scala Release Schedule update, Scala 2.12 Roadmap.