Julia dispose enfin d’un débogueur !

Malgré la sortie de la version 1.0 du langage de programmation Julia, on ne disposait toujours pas d’un débogueur, dont le développement était en cours depuis des années (Gallium.jl). C’est maintenant chose faite : on dispose d’un débogueur interactif (tant avec une interface graphique qu’en ligne de commande) pour Julia. Celui-ci dispose de points d’arrêt (quelque chose qui a longtemps manqué aux prototypes précédents), notamment lorsqu’une erreur survient ; on peut interagir, exécuter la ligne suivante, etc. Aussi, en pleine session de débogage, on peut modifier le code, tester un correctif et s’assurer qu’il fonctionne bien, sans devoir recommencer toute la session de zéro. Ces fonctionnalités sont disponibles dans l’environnement de développement Juno.

L’interface la plus puissante est néanmoins celle proposée en ligne de commande. On distingue deux approches assez opposées : d’un côté, Debugger.jl (image ci-dessous) propose une interface à base de commandes comme “aller à la ligne suivante” ou “continuer. Ce paquet est assez avancé dans ses possibilités, on peut avoir un contrôle très fin de l’avancée de l’exécution ou entrer dans les détails du générateur pour les fonctions générées (indiquées par le mot clé @generated : les fonctions générées voient leur code obtenu par l’exécution d’une fonction, ce qui donne des possibilités de métaprogrammation extrêmement avancées). On commence une session de débogage en annotant la fonction à lancer avec @enter.

De l’autre, Rebugger.jl propose un nouveau mode dans l’interpréteur (accessible par Alt+I). Contrairement à Debugger.jl, qui fonctionne au niveau d’une fonction, Rebugger.jl travaille avec des expressions. Il peut aussi éditer le code en live (Alt+E).

Ces deux moteurs de débogage sont implémentés comme des paquets Julia, au même titre qu’une bibliothèque de graphiques ou de calcul symbolique. Il n’y a donc pas vraiment d’intégration poussée avec le moteur d’exécution de Julia — tout simplement parce qu’il n’y en a pas besoin, grâce aux possibilités de métaprogrammation offertes par le langage.

Les deux sont construits par-dessus JuliaInterpreter.jl, un interpréteur pour le langage Julia, lui-même écrit en Julia. En effet, malgré son interface agréable en ligne de commande, Julia est implémenté comme un langage compilé à la volée (JIT), pour garantir une excellente performance — au détriment de la facilité de débogage, par exemple.

Ensuite, LowerCodeUtils.jl permet de travailler avec les représentations internes au compilateur du code en cours d’exécution (plus précisément, avec l’arbre syntaxique abstrait, AST). Revise.jl se focalise sur la mise à jour du code : par le fonctionnement compilé de Julia, dès qu’on a importé un paquet, même si les sources changent (parce qu’on a changé de branche pour passer à une version de développement, par exemple), il n’est pas recompilé. CodeTracking.jl fournit une interface par-dessus Revise.jl, notamment pour suivre l’évolution des numéros de ligne où sont définies les fonctions.

Source : A Julia interpreter and debugger.

Advertisements

Sortie de JuMP 0.19, la couche de modélisation mathématique pour Julia

Julia a une longue histoire avec les applications scientifiques, y compris l’optimisation mathématique. En effet, une couche de modélisation existe depuis des années — JuMP (Julia Mathematical Optimisation). Ce projet est d’ailleurs l’un des plus utilisés dans l’écosystème Julia, JuMP étant actuellement le neuvième paquet ayant le plus d’étoiles sur GitHub.

La version 0.19 de JuMP vient de sortir. Elle marque un nouveau tournant dans l’histoire du projet, puisque presque tout le code a été réécrit — même si peu de choses sont visibles du côté utilisateur. Cette version est en cours de développement depuis plus d’un an. Le principal changement concerne la couche de communication entre JuMP et les solveurs d’optimisation : MathProgBase est remplacé par MathOptInterface. Cette nouvelle interface corrige bon nombre de défauts de conception mis au jour au fil du temps et permet, notamment, de supprimer des variables et des contraintes (chose impossible avec MathProgBase), entre autres. Cela signifie que le lien avec les solveurs doit être réécrit, ce qui n’a pas encore été fait pour tous les solveurs.

Le principal changement de syntaxe par rapport aux versions précédentes se montre ici : au lieu de créer un modèle d’optimisation avec Gurobi (par exemple) comme solveur, avec une ligne comme Model(solver=GurobiSolver()), il faudra préférer Model(with_optimizer(Gurobi.Optimizer)). Les codes de retour des solveurs ont aussi changé, pour mieux rendre compte de la diversité des informations disponibles.

Ce changement d’interface sert notamment à mieux gérer différents types de contraintes, coniques notamment. JuMP peut maintenant gérer n’importe quel type de contrainte sans besoin d’une syntaxe spécifique : de manière générale, une contrainte indique qu’une certaine expression doit prendre une valeur dans un certain ensemble. Cela signifie notamment la fin de certains raccourcis dans l’écriture : norm() n’est plus disponible, il faudra maintenant utiliser directement un cône de Lorentz avec SecondOrderCone. De manière générale, JuMP effectue aussi peu de conversions que possible, de telle sorte que le modèle entré par l’utilisateur soit exposé assez directement au solveur.

Bon nombre de nouvelles fonctionnalités font aussi leur apparition, principalement grâce à cette nouvelle interface : la suppression de contraintes et de variables, les modèles coniques mélangeant différents types de cônes (y compris puissance et exponentielle), la possibilité de donner des valeurs duales initiales, l’accès grandement étendu aux attributs offerts par le solveur (sans devoir utiliser l’API de bas niveau).

Les types des conteneurs ont été entièrement repensés, en favorisant la performance ou la possibilité de l’améliorer dans les versions à venir. La source principale d’inspiration a été AxisArrays. JuMP définit maintenant les types SparseAxisArray et DenseAxisArray, selon le type d’indexation possible (ces types correspondent respectivement à JuMPDict et à JuMPArray).

Cette version 0.19 n’est certes pas loin d’une première version finale pour JuMP, mais n’est pas non plus finie. Il manque une série de fonctionnalités, plus ou moins importantes selon les utilisateurs (et qui étaient disponibles auparavant), comme la génération de colonnes, l’accès direct au relâchement continu ou aux fonctions de rappel des solveurs (un mécanisme qui n’est plus générique, mais bien spécifique à chaque solveur) ou encore la réoptimisation rapide de programmes non linéaires. La performance lors de la génération des modèles peut être en-deçà des possibilités des versions précédentes de JuMP, mais les développeurs sont au courant.

Source : les notes de version.

Sortie de Julia 1.1 RC 1

Le développement de Julia, langage de programmation dynamique de haute performance, se poursuit. La version 1.0 devait être la base du langage pour les versions à venir sans perte de rétrocompatibilité : aucune fonctionnalité ne peut disparaître d’ici à Julia 2.0. Cette garantie est prévue pour faciliter l’utilisation à plus grande échelle du langage.

Julia 1.1 apporte peu de nouvelles fonctionnalités, mais celles-ci devraient simplifier la vie des développeurs. Tout d’abord, les exceptions peuvent désormais s’empiler : si le code lance une exception pendant le traitement d’une exception, les deux sont gardées dans une pile. Auparavant, la première exception était écrasée par la seconde. Cela permettra de déboguer plus facilement le code, en remontant directement à la cause première. La méthode catch_stack est disponible pour accéder à cette pile ; pour le moment, l’implémentation est expérimentale et l’API pourrait changer. L’opérateur ~ est souvent utilisé en probabilités pour indiquer qu’une variable aléatoire suit une certaine distribution de probabilité ; Julia dispose de cet opérateur depuis un certain temps, mais celui-ci ne pouvait pas s’appliquer sur des vecteurs : le compilateur ne comprenait pas la forme .~, ce qui est maintenant le cas.

Les méthodes findmin, findmax et findall ne retournaient pas toujours le même type pour indiquer quels éléments correspondent à la requête. Désormais, quand la recherche se fait dans une matrice ou un tableau d’ordre supérieur, ces méthodes renvoient des objets CartesianIndex. Ce changement casse légèrement la compatibilité avec le code existant, mais il a été estimé qu’il aurait dû être effectué pour la 1.0 et que c’était donc un défaut.

La bibliothèque standard s’est enrichie d’une série de méthodes. Par exemple, splitpath pour convertir un chemin en ses différentes composantes ; isnothing pour déterminer si l’argument est nothing ou non (par exemple, une donnée absente) ; eachrow, eachcol et eachslice pour itérer efficacement sur des morceaux de tableaux ; les systèmes BSD disposent d’une série de méthodes pour les détecter et, au besoin, adapter le code.

Voir aussi : la liste complète des changements.

JuMP.jl : la longue voie vers Julia 1.0

Avec la sortie de Julia 1.0, c’est tout un écosystème qui doit se mettre à jour — notamment JuMP, un langage dédié (DSL) à l’optimisation mathématique totalement intégré à Julia. Il permet de transformer un modèle mathématique en un code extrêmement compact et proche des notations mathématiques (d’une manière similaire à AMPL, par exemple), tout en profitant de l’intégration à Julia et d’une excellente performance (contrairement à Pyomo, en Python).

Le projet a débuté au MIT, tout comme le langage Julia, en tant que collaboration entre la Sloan School of Management et le Operations Research Center. Il n’était qu’une petite expérience pour voir les améliorations possibles dans le domaine des langages dédiés à l’optimisation mathématique, mais a fini par grandir au point de générer un écosystème à part entière (JuliaOpt), avec le développement d’autres paquets pour faciliter la modélisation, ainsi que de solveurs complets. Désormais, JuMP est utilisé de manière industrielle, par exemple pour organiser le ramassage scolaire à Boston (États-Unis), les horaires en entreprise ou encore les trains au Canada ; il a aussi gagné bon nombre de cours dans le domaine. Un signe de son utilité à la communauté, la fondation NumFOCUS (qui se concentre sur le calcul numérique au sens large) servira de sponsor fiscal à JuMP et à JuliaOpt : cette fondation (reconnue 501(c)3 aux États-Unis, c’est-à-dire sans but lucratif) gérera la réception des dons et l’émission des reçus, par exemple.

En peu de mots : la version 0.18 (l’actuelle) ne fonctionne qu’avec Julia 0.6, mais sera encore maintenue un certain temps ; la version 0.19 (la suivante) inclut des modifications très profondes dans la structure de JuMP et ne sera pas prête de suite. Cette dernière sera compatible tant avec Julia 0.6 que 0.7, afin de faciliter la migration du code. La version 0.18  devrait être rendue compatible avec Julia 0.7, mais les développeurs de JuMP n’estiment pas cet effort et n’envisagent pas d’y investir beaucoup de temps.

Les changements prévus consistent en un changement complet de la couche de communication avec les solveurs d’optimisation mathématique (comme CPLEX, Gurobi ou Mosek). Ainsi, MathProgBase (MPB) cédera la place à MathOptInterface (MOI). Les contraintes y sont spécifiées de manière beaucoup plus génériques : la valeur d’une certaine fonction doit appartenir à un certain ensemble (quand MathProgBase utilisait des objets spécifiques pour chaque type de contrainte, sans réelle possibilité d’extensibilité : les contraintes linéaires, quadratiques, etc.). Le système de récupération du statut du solveur à la fin de son exécution a complètement changé, afin de représenter les possibilités aussi finement que possible (pour indique,r par exemple, la raison pour laquelle le solveur s’est arrêté avant d’obtenir une solution optimale : a-t-il atteint la limite de temps ?). Tous les paramètres sont aussi passés sous la forme d’attributs, ce qui facilite l’ajout de nouveaux paramètres pour les solveurs sans toucher au code existant. Une fonctionnalité était attendue depuis longtemps : la modification de contraintes.

En pratique, cela veut dire que toutes les extensions de JuMP devront être complètement réécrites (et repensées). Pour l’utilisateur, cependant, les changements seront plus limités : la récupération du code de statut, l’accès aux valeurs duales, les fonctions de rappel (qui ne seront plus du tout implémentées par JuMP, de par la disparité entre les types de fonctions implémentées par chaque solveur — ce point sera le plus problématique). Cependant, les changements ne seront pas finis : les modèles purement non linéaires (c’est-à-dire sans structure supplémentaire, pour lesquels JuMP doit calculer par exemple des gradients à partir du code fourni par l’utilisateur) sont presque entièrement repris de MathProgBase.

Sources : JuMP and Julia 0.7/1.0, MathOptInterface and upcoming breaking changes in JuMP 0.19, Optimization modeling language JuMP joins NumFOCUS Sponsored Projects.

Sortie de Julia 1.0

Julia est un langage novateur à certains points de vue : il atteint une vitesse d’exécution comparable au C, au C++ et à Fortran ; il est flexible et dynamique comme Ruby ou Python (pas de typage statique obligatoire, possibilité de définir de nouveaux types à l’exécution, etc.), tout en disposant d’un système de macros très puissant inspiré de LISP (qui permet de réécrire complètement du code). Sa syntaxe est prévue pour facilement écrire des mathématiques, y compris très avancées (ainsi, tout caractère Unicode peut servir pour les noms de variable ou de fonction, sauf rare exception, comme α, x² ou ∇, un grand nombre de caractères peut aussi être utilisé comme opérateurs, comme ∪, ∨, ±, ⊗ ou encore ⋉), tout en gardant un langage générique (à l’instar de Python) — on compte, par exemple, plusieurs frameworks Web (Genie.jl, Mux.jl). Sa licence est aussi extrêmement permissive, la grande majorité du code étant disponible sous MIT (seules quelques dépendances utilisent la GPL, mais elles peuvent être désactivées à la compilation).

Après six ans de développement public (et près de deux millions de téléchargements) — neuf depuis le début de la conception du langage —, voici venu le temps de Julia 1.0, la première version qualifiée par les développeurs de stable. La principale nouveauté par rapport aux versions précédentes est l’assurance qu’il n’y aura plus de changement de syntaxe pendant tout le cycle de versions 1.x : le langage continuera à évoluer, sa bibliothèque standard aussi, mais sans casser l’existant. Les développeurs ne rejettent pas l’idée d’une version 2.0, mais celle-ci prendra probablement plusieurs années à venir : toutes les fonctionnalités qui pouvaient poser problème et qui ont été accumulées sur les années de développement ont été désapprouvées avec la version 0.7. À ce niveau, Julia 1.0 est un nouveau départ plus propre pour la suite du développement.

Une autre nouveauté majeure est le remplacement du gestionnaire de paquets. Pkg2 officiait depuis plusieurs années et montrait quelques limites. La nouvelle itération, simplement nommée Pkg (anciennement Pkg3), fonctionne avec la notion d’environnements, qui peuvent coexister sur un même système sans interférer dans les versions des bibliothèques d’autres environnements (tout comme les environnements virtuels de Python).

Pour la gestion des données, Julia dispose maintenant d’une représentation canonique, comme NA en R. Le type d’une variable dont la valeur peut manquer est représenté par une union : Union{Missing, T}, c’est-à-dire que la valeur peut être soit missing (qui indique une valeur manquante), soit une valeur normale. Cette représentation fonctionne désormais très bien (elle est autorisée depuis les premières versions de Julia), grâce à de nombreuses optimisations au niveau du compilateur. Ainsi, Julia atteint la vitesse d’exécution de systèmes conçus d’une manière plus spécifique, tout en gardant une très grande flexibilité.

La gestion des chaînes de caractères évolue à nouveau (c’était déjà l’un des grands changements de Julia 0.4), avec le type String. Celui-ci peut stocker n’importe quelle suite d’octets, pas forcément des codes Unicode valides : toutes les données sont préservées, mais Julia indique quels octets correspondent à des valeurs autorisées selon Unicode.

Au niveau de la bibliothèque standard, un gros effort de modularisation a été mené : elle a été divisée en plus petits paquets qui sont toujours livrés avec Julia, mais qu’il faut importer soi-même (auparavant, ces fonctionnalités étaient disponibles d’office). L’objectif est que, à terme, ces modules puissent avoir un versionnage complètement indépendant de Julia et évoluer bien plus vite que le langage. En parallèle, toutes les fonctions ont été revues pour assurer un plus grand niveau de cohérence : ordre des arguments, nommage des fonctions (notamment pour éviter des noms hérités du C et qui n’ont plus de sens dans un langage moderne).

Ces évolutions et d’autres ont permis l’évolution de l’écosystème de Julia. Notamment, missing est à l’origine d’une réécriture de certains paquets de gestion des données. Cassette.jl est un paquet extrêmement puissant (mais complexe à utiliser dans la même mesure) qui permet d’effectuer des transformations du code juste avant sa compilation (d’une manière plus flexible que les macros). Ce dernier continuera à évoluer avec les prochaines versions de Julia, notamment au niveau de la performance, pour atteindre le même niveau de performance que d’autres approches de la programmation générique (par exemple, pour la différentiation automatique ou l’analyse dynamique de code).

Julia 1.0 sort en même temps que la version 0.7. Au niveau des fonctionnalités, les deux sont identiques : la 0.7 est prévue pour aider à la migration depuis Julia 0.6, grâce à de nombreux messages d’avertissement ; ainsi, le même code peut fonctionner soit avec Julia 0.6 et 0.7, soit avec la 0.7 et la 1.0. Julia 1.0 est ainsi plus léger, vu qu’il ne doit plus gérer la syntaxe antérieure (ce qui ralentit aussi l’exécution du code). Ainsi, pour les nouveaux utilisateurs du langage, il vaut mieux commencer directement par la version 1.0.

Néanmoins, cette sortie de Julia 1.0 ne fait pas l’unanimité dans la communauté. En effet, les dernières versions sont sorties dans la précipitation : en mois de deux semaines, la 0.7 a eu trois préversions RC (dont une qui n’a jamais été annoncée à cause de problèmes majeurs trouvés rapidement) et une pour la 1.0. De plus, certains utilisateurs ont la sensation que l’écosystème n’est pas encore vraiment prêt pour une version 1.0 : certains paquets absolument nécessaires pour des tâches courantes (comme l’accès aux bases de données) ne sont absolument pas au niveau, d’autres devraient avoir une petite mise à jour avant d’être pleinement compatibles avec Julia 0.7 et 1.0.

Voir aussi : la liste des nouveautés depuis Julia 0.6.

Télécharger Julia 1.0 et 0.7.

Source : Julia 1.0.

Interact.jl, la nouvelle manière de réaliser des sites Web interactifs pour accéder à des codes de calcul

Julia est un langage de programmation principalement prévu pour des applications scientifiques, où la quantité de calculs à effectuer est très importante. D’habitude, ce genre de code s’écrit dans un langage assez statique, comme C, C++ ou Fortran, pour profiter de leur performance ; cependant, Julia se focalise sur une approche plus dynamique (comme Python ou MATLAB, plus prévus pour les interfaces que le calcul intensif) tout en gardant la performance à l’exécution. Julia arrive à rassembler ces deux côtés, la performance et la dynamicité.

Cet aspect dynamique a déjà été exploité pour créer des interfaces graphiques par le Web qui exploitent des codes de calcul. Par exemple, Escher.jl proposait de créer des interfaces composables, développées d’une manière similaire à Shiny en R. Ce paquet n’est plus maintenu depuis 2016 et n’a de remplaçant digne de ce nom que depuis peu, avec Interact.jl nouvelle génération (ce projet s’appelait InteractBase.jl jusqu’il y a peu avant d’être renommé et de remplacer l’ancien Interact.jl).

Ce nouveau paquet propose de créer des interfaces graphiques sur le Web pour des applications scientifiques. Il se construit par-dessus WebIO.jl, une couche d’abstraction pour l’interaction à travers les protocoles du Web : on peut ainsi développer un seul code pour l’utiliser dans un navigateur Web traditionnel (à travers le serveur Mux.jl), dans une application de bureau créée avec Electron (grâce à Blink.jl) ou encore dans Juno, l’environnement de développement de référence actuel pour Julia, ou IJulia.

Interact.jl utilise HTML5 et ses composants pour ses widgets, ce qui permet de couvrir facilement un grand nombre de fonctionnalités (sélection de couleur, de date, d’heure, de texte, etc.). Des frameworks CSS sont utilisés pour le style, qui est alors entièrement abstrait du code de l’interface : pour le moment, on peut utiliser Bulma et UIkit, mais aussi utiliser d’autres frameworks (UIkit a été ajouté en moins de deux cents lignes de code) ou encore intégrer son propre code CSS pour modifier un framework existant. Vue.js/.jl est utilisé pour la logique JavaScript et la synchronisation avec le script Julia.

Voir aussi : le tutoriel officiel, la liste des widgets disponibles, la documentation.

Sources : ANN: InteractBase, a redesign of Interact to create and style web apps in Julia, Sputnik project, second update.

Sortie de Julia 0.7 Alpha

Le langage de programmation Julia est prévu pour apporter les bienfaits en termes de productivité des langages dynamiques au monde extrêmement conservateur du calcul de haute performance. La première préversion Alpha du langage en version 0.7 est maintenant disponible et elle sera assez proche de la version 1.0. Elle contient toutes les fonctionnalités désapprouvées du langage, avec des avertissements (ces constructions donneront directement des erreurs avec Julia 1.0, prévue pour début août) : cette version 0.7 n’a que pour objectif de faciliter la transition de l’écosystème vers la 1.0. Les développeurs de Julia se sont inspirés des problèmes que Python a eu en passant de la version 2 à 3 : il sera possible, pour une très grande majorité des paquets, d’avoir un même code fonctionnel sur la version 0.6 (l’actuelle), la 0.7 ou sur la 0.7 et la 1.0.

La liste des changements par rapport à la version 0.6 est impressionnante : elle représente quatre-vingt cinq kilooctets de texte !

Syntaxe

Une bonne partie des changements de cette version vient de la syntaxe du langage. Ainsi, la syntaxe de définition des fonctions est plus flexible. Les arguments peuvent être déstructurés automatiquement par le langage : range((min, max)) = max - min définit automatiquement une fonction à un seul argument, range, cet argument étant un tuple. L’avantage est que, dans le code de la fonction, on peut utiliser chaque élément de ce tuple sans devoir le déstructurer soi-même : une fonction aussi simple peut être écrite en une ligne, en gagnant en lisibilité. Cette syntaxe se mélange sans aucun problème avec d’autres arguments :

foo((a, b), c) = (a + b) * c

Toujours dans les définitions de fonction, le conteneur d’arguments nommés (généralement, kw) est maintenant implémenté comme un tuple nommé (plutôt qu’une liste de paires). Ce conteneur apparaît lorsqu’une fonction ne définit pas tous ses arguments nommés : f(; kw...), par exemple. Les fonctions habituelles des dictionnaires fonctionnent alors naturellement : au lieu de devoir itérer dans toutes les entrées manuellement, on peut simplement utiliser haskey(kw, "arg"). Ceci n’est rendu possible que par l’implémentation des tuples nommés avec une excellente performance (qui a empêché cette manière de procéder jusqu’à présent). Petit supplément : une fonction peut maintenant marquer un argument nommé comme obligatoire, simplement en ne définissant pas sa valeur par défaut (ce qui peut être très utile pour la lisibilité du code).

L’objet missing sert désormais à représenter des valeurs manquantes (comme NULL en SQL ou NA en R, voire comme NaN pour le calcul en virgule flottante). Elle se propage naturellement dans les opérateurs et les fonctions mathématiques selon une logique à trois valeur (1 + missing == missing, par exemple). Cet objet a pour type Missing. Il peut simplifier très largement des parties de code qui utilisaient Nullable{T} comme type pour gérer des données qui pourraient manquer : le type de ces données devrait maintenant être Union{T, Missing}, ce qui est aussi clair ; dans le code, il n’est plus nécessaire d’utiliser des artefacts comme get(v) pour récupérer la valeur dans le cas où une valeur existe.

Aussi, la gestion des opérateurs a été dopée. Outre le nouvel opérateur de comparaison , quelques caractères de combinaison d’Unicode (primes, exposants, indices) peuvent se combiner aux opérateurs prédéfinis. Par exemple, on peut ainsi définir le nouvel opérateur de somme des carrés : +₂(a,b) = a^2 + b^2. Ces possibilités seront utiles à certains paquets effectuant des mathématiques de haut vol, en rapprochant la syntaxe de la notation mathématique usuelle. Fidèle à son habitude, Julia ne rate donc pas une occasion de gérer une plus grosse partie d’Unicode. Toujours pour les opérateurs, mais en mode mineur, l’opérateur paire => (utilisé pour définir des dictionnaires, par exemple) peut être diffusé sur des listes (.=>).

Pour l’appel des macros, il devient possible d’utiliser des crochets en plus des parenthèses : @macroname[args] correspond à @macroname([args]). Cette possibilité est prévue pour l’implémentation facile de nouveaux types de tableaux, par exemple des tableaux statiques (StaticArrays.jl) : il est plus naturel d’écrire @SArray[1 2; 3 4] que @SArray([1 2; 3 4]).

Les énumérations peuvent être définies dans un bloc begin-end, plutôt que de devoir être définies sur une seule ligne :

@enum Fruit begin
	apple=1 
	orange=2
	kiwi=4
end

Auparavant, il fallait impérativement écrire @enum Fruit apple=1 orange=2 kiwi=4, peu importe le nombre d’items.

La macro @isdefined permettra de déterminer si une variable locale est définie. Les tuples nommés, comme t=(a=1, b2=), font leur apparition : leurs champs peuvent être accédés par un numéro (t[1]) ou par un nom (t.a). De même, dans les fonctions générées (elles prennent en argument les types des arguments et génèrent un code hautement spécialisé), il devient possible de définir une fonction qui est à la fois générée et non générée (pour une implémentation non spécialisée) en utilisant la macro @generated pour délimiter les parties générées des autres.

Certaines syntaxes sont désapprouvées, en vue de leur assigner une nouvelle signification plus tard. Par exemple, begin ne peut plus apparaître dans une indexation : dans le futur, a[begin] pourrait sélectionner le premier item d’un tableau (tout comme a[end] sélectionne le dernier élément). Il n’est plus possible d’effectuer d’assignation au niveau d’une indexation : x[i=1] sélectionnait l’élément 1 du tableau et définissait une variable i (certains utilisaient cette possibilité pour se rapprocher de leurs habitudes en C, comme a[i += 1] pour sélectionner l’élément courant et incrémenter i) ; bientôt, cette syntaxe pourrait être récupérée pour passer des arguments nommés lors de l’indexation, ce qui serait utile pour la lisibilité de certains types de tableaux (notamment bidimensionnels : on pourrait écrire a[x=1, y=2], comme xarray en Python).

Bibliothèque standard

Le gestionnaire de paquets précédent, Pkg2, était un reliquat de Julia 0.2. Il avait un certain nombre de limitations, notamment pour indiquer des dépendances conditionnelles entre modules (par exemple, pour des fonctions dont leur seul objectif est de faciliter l’utilisation conjointe de deux modules). La description des paquets se faisait par une myriade de petits fichiers (quelques octets), ce qui faisait rapidement une performance horrible selon le système de fichiers ; en particulier, une version est décrite par un identifiant de commit Git, ce qui impose de cloner le dépôt Git d’un paquet pour l’installer (y compris tout l’historique, même s’il est très gros), puis d’utiliser Git pour vérifier la version installée (ce qui est d’une lenteur abyssale). Ces caractéristiques étaient très intéressante dans le cas d’un écosystème en plein boom et où la proportion d’utilisateurs qui ne contribuent pas à de nouveaux paquets est assez faible, mais plus à un langage qui se veut utilisable par le plus grand nombre. Pkg3 évite ces écueils en repartant de zéro pour la description et l’installation de paquets. Le dépôt de paquets actuel est bien sûr importé directement dans celui de Pkg3 (une liste modérée des “meilleurs” paquets sera aussi disponible). Pkg3 gère aussi la notion d’environnement, c’est-à-dire un ensemble de paquets et de versions que l’on peut échanger rapidement (comme les environnements virtuels de Python).

Le protocole d’itération a complètement changé avec cette version, notamment en exploitant le travail effectué pour intégrer missing dans le langage avec une bonne performance. Auparavant, il fallait implémenter trois fonctions différentes : start pour déterminer l’état initial de l’itérateur, next pour récupérer l’élément courant et le prochain état, done pour indiquer si l’état est final. Maintenant, il n’y a plus que la fonction iterate : avec un seul argument (l’itérateur), elle doit retourner l’état initial et le premier élément ; avec deux arguments (l’itérateur et l’état), elle doit retourner le prochain élément et le prochain état… ou nothing si l’itérateur n’a plus d’éléments dans la collection.

La bibliothèque mathématique de Julia est en cours de réécriture : elle était écrite entièrement en C (openlibm), en repartant du code de FreeBSD et d’OpenBSD, mais avait ses limitations. Notamment, si une implémentation d’une fonction spéciale (logarithme, exponentielle, sinus, etc.) est fournie, elle ne vaut que pour un seul type ; si elle était écrite en Julia, la même implémentation pourrait être utilisée avec d’autres types (nombre à virgule flottante sur trente-deux ou soixante-quatre bits, voire à précision infinie, par exemple). La charge de maintenance est identique (le projet Julia doit toujours maintenir sa propre bibliothèque mathématique, celles des compilateurs n’étant pas toujours au niveau), mais les avantages sont présents — notamment celui de diminuer la barrière d’entrée pour les nouveaux contributeurs. Ce travail est toujours en cours.