1. Premières lignes▲
La première chose à faire pour tester Julia, c'est probablement de l'installer. Ceux qui préfèrent s'économiser cette peine peuvent toutefois l'utiliser en ligne : JuliaBox propose une interface Web basé sur Jupyter (ex-IPython), entièrement gratuite.
Ensuite, testons quelques commandes ! Julia peut-il effectuer des calculs basiques ? Bien évidemment :
julia>
1
+
1
2

Jusque-là, rien de très étonnant. On peut aussi afficher un peu de texte à l'écran en utilisant la fonction println :
julia>
println(
"Hello, World!"
)
Hello, World!
Quelques remarques. Pour appeler une fonction, on utilise son nom, puis des parenthèses : on insère entre celles-ci les arguments à passer à la fonction. Rien de très étonnant, c'est comme cela que les choses se passent dans la majorité des langages de programmation. Les chaînes de caractères s'indiquent exclusivement entre guillemets doubles : ". Les guillemets simples, ', servent quant à eux à indiquer un caractère simple : Julia est très précis sur les types et fait la distinction entre un caractère (comme 'a') et une chaîne d'un seul caractère ("a"). La fonction typeof donne le type de son argument :
julia>
typeof(
"a"
)
String
julia>
typeof(
'a'
)
Char
En tant que langage d'abord prévu pour les applications mathématiques, on s'attend à ce que le système de types soit assez robuste : une chaîne de caractères contenant un nombre ne devrait pas être confondue avec ce nombre. Une opération d'addition entre la chaîne de caractères "42.0" et le nombre à virgule flottante 1.0 n'est ainsi pas permise :
julia>
"42.0"
+
1.0
ERROR: MethodError
: no method matching +
(
::
String
, ::
Float64
)
Closest candidates are:
+
(
::
Any
, ::
Any
, ::
Any
, ::
Any...)
at operators.jl:502
+
(
::
Bool
, ::
T<:
AbstractFloat
)
where
T<:
AbstractFloat
at bool.jl:112
+
(
::
Float64
, ::
Float64
)
at float.jl:395
...
Stacktrace:
[1
] top-
level scope at none:0
Vous remarquerez au passage la longueur du message d'erreur : autant que possible, Julia vous donne toutes les informations nécessaires en cas d'erreur. Cela n'a pas toujours été le cas avec les versions préliminaires, mais les développeurs de Julia y prêtent une certaine attention depuis la version 1.0.
Ce message est assez courant : il indique qu'il n'existe pas de méthode dont le nom est + et qui prend, en argument, une chaîne de caractères (String) et un nombre à virgule flottante (Float64). Par contre, l'interpréteur connaît plusieurs méthodes portant ce nom, mais avec d'autres arguments.
On peut voir ici quelques spécificités de Julia. Le langage fait la distinction très nette entre les fonctions et les méthodes : une fonction n'est qu'un nom pour une opération (comme + ou println), tandis qu'une méthode est une implémentation de cette fonction. À l'exécution, Julia choisit la méthode à appeler en fonction du type de chacun des arguments. Par exemple, si on tente d'additionner un nombre à virgule flottante (Float64) avec un nombre entier (Integer), Julia regarde les méthodes disponibles pour la fonction + : on peut additionner directement un Float64 avec un Integer, mais il existe des méthodes plus spécifiques au niveau des types, selon qu'on a un Int64 (entier signé sur 64 bits), UInt64 (entier non signé sur 64 bits), etc. Le moteur d'exécution choisira toujours la méthode la plus précise, au moment où la fonction doit être appelée. Pour faire du polymorphisme à l'exécution, contrairement aux langages statiquement typés, les arguments ne sont pas obligés d'avoir un type statique à partir duquel le langage détermine quelle implémentation choisir à l'exécution(1).
julia>
methods(
+
)
# 163 methods for generic function "+":
[1
] +
(
x::
Bool
, z::
Complex
{
Bool
}
)
in
Base
at complex.jl:277
[2
] +
(
x::
Bool
, y::
Bool
)
in
Base
at bool.jl:104
[3
] +
(
x::
Bool
)
in
Base
at bool.jl:101
[4
] +
(
x::
Bool
, y::
T)
where
T<:
AbstractFloat
in
Base
at bool.jl:112
[5
] +
(
x::
Bool
, z::
Complex
)
in
Base
at complex.jl:284
[6
] +
(
a::
Float16
, b::
Float16
)
in
Base
at float.jl:392
[7
] +
(
x::
Float32
, y::
Float32
)
in
Base
at float.jl:394
[8
] +
(
x::
Float64
, y::
Float64
)
in
Base
at float.jl:395
[9
] +
(
z::
Complex
{
Bool
}
, x::
Bool
)
in
Base
at complex.jl:278
[10
] +
(
z::
Complex
{
Bool
}
, x::
Real
)
in
Base
at complex.jl:292
…
2. Installation de Juno▲
Utiliser Julia uniquement en ligne de commande n'est pas le plus pratique, même si l'interpréteur dispose d'un grand nombre de fonctionnalités. Bon nombre d'utilisateurs préfèrent un environnement de développement. L'écosystème Julia en dispose de deux : Juno, basé sur Atom (l'EDI développé par GitHub) est le plus développé (avec une interface qui rappelle RStudio ou MATLAB : on dispose d'une zone de code, d'un accès direct à l'interpréteur, d'une zone pour visualiser des graphiques, la documentation ou encore les variables définies dans l'interpréteur) ; Visual Studio Code est un autre environnement de développement, cette fois-ci proposé par Microsoft, par défaut un peu plus rustre. Si vous préférez, il existe des extensions pour d'autres environnements : IDEA (IntelliJ, PyCharm, CLion, etc.), Sublime Text. Les éditeurs de texte en ligne de commande ne sont pas non plus en reste : tant Vim qu'Emacs disposent d'extensions.


Pour ce tutoriel, on utilisera Juno, même s'il est un peu plus compliqué à installer que Visual Studio Code. Pour ceux qui préféreraient néanmoins ce dernier, il suffit d'installer l'environnement, puis l'extension Julia.
L'installation de Juno se passe en deux étapes. Il faut d'abord télécharger Atom sur le site officiel :

Une fois l'installateur téléchargé, lancez-le. Celui-ci installe Atom pour l'utilisateur courant, c'est-à-dire qu'il n'y a pas besoin de droits d'administrateur sur la machine.

Au premier lancement, Atom propose une série d'actions à réaliser :

Sélectionnez l'option d'installation de paquets, puis cliquez sur Open Installer pour lancer l'installateur de paquets.

Dans la colonne de gauche, cherchez uber-juno :

Vous pourrez revenir, par la suite, à cet écran en utilisant le raccourci Ctrl+,, puis en sélectionnant l'option Install (ce qui est ici déjà fait).
Cliquez sur le bouton Install et attendez que l'installation se passe.


À la fin du processus, une petite fenêtre vous demande si vous souhaitez utiliser l'interface adaptée pour Julia. Si vous refusez, vous retrouverez l'interface habituelle d'Atom, notamment sans accès direct à l'interpréteur Julia.

Et voilà, Juno est disponible et prêt à l'utilisation !

Si vous souhaitez installer un autre thème que le sombre affiché par défaut, utilisez le raccourci Ctrl+,, sélectionnez l'entrée Themes.

Choisissez le thème que vous préférez pour l'interface (UI Theme) et la syntaxe du code (Syntax Theme). Par exemple, en choisissant deux fois One Light (la version claire du thème par défaut), vous obtiendrez ce rendu :

3. Gestion des paquets▲
L'une des forces de Julia, c'est son écosystème très dynamique : en permanence, on voit de nouveaux paquets apparaître. JuliaObserver offre un moteur de recherche dans ces paquets, ainsi qu'une catégorisation et des tendances. Il est très facile d'y faire entrer un paquet : il suffit de l'enregistrer dans le dépôt JuliaRegistries. N'importe qui ne peut donc pas ajouter un paquet, tout ajout est vérifié avant d'être disponible au grand public.
Selon vos intérêts, différents paquets pourraient vous intéresser. La majorité est orientée vers le calcul scientifique, au sens large, mais on peut utiliser Julia dans n'importe quelle application :
-
pour le développement Web, la communauté JuliaWeb fournit un bon nombre de fonctionnalités de base ; Genie s'impose comme framework de développement d'applications ;
-
pour la visualisation de données, Plots est la référence ;
-
pour les interfaces graphiques, Blink offre la possibilité d'utiliser les technologies du Web ;
-
pour travailler avec des graphes, LightGraphs est une bibliothèque très développée, avec un bon nombre d'extensions pour les besoins plus spécifiques ;
-
pour le traitemente d'images, JuliaImages fournit un bon nombre de paquets intéressants ;
-
pour l'apprentissage automatique et la science des données en général, DataFrames fournit une structure de données tabulaire qui facilite les requêtes et autres traitements ; Query fournit un langage de type SQL pour effectuer des requêtes sur un grand nombre de structures de données ; Flux offre des réseaux neuronaux très performants ; ScikitLearn offre d'autres algorithmes d'apprentissage ;
-
pour l'optimisation mathématique, JuMP est de plus en plus reconnu comme meneur (et pas qu'en Julia) ;
-
pour les équations différentielles, JuliaDiffEq est une interface de programmation et regroupe les solveurs les plus performants actuellement (implémentés en Julia pour la plupart).
Il reste une question à se poser : comment installer un paquet ? Le gestionnaire de paquets s'appelle en appuyant sur ]. De là, la commande add sert à ajouter des paquets et rm à en désinstaller. Le gestionnaire de paquets gère bien évidemment toutes les dépendances entre paquets.
Une fois installé, vous pouvez importer le contenu d'un paquet avec l'instruction using. Par exemple, pour Plots, utilisez using Plots : toutes les fonctions définies par le paquet, comme plot(), seront alors disponibles.

4. Éléments de syntaxe▲
Julia utilise un syntaxe sans indentation (à la Python) ni accolades (à la C et famille) : un bloc commence avec un mot clé (if, while, begin, etc.) et se termine par end. Par exemple, pour boucler sur une série de nombres et les afficher :
for
i in
1
:10
println(
i)
end
Les boucles for ne fonctionnent pas comme dans la majorité des langages : Julia préfère itérer sur un conteneur qu'incrémenter manuellement une variable ; il n'existe pas de forme avec trois arguments (initialisation, condition, mise à jour, comme en C). Ici, la boucle itère sur une plage de nombres, entre 1 et 10, avec un incrément d'une unité. On pourrait aussi itérer sur les éléments d'un tableau :
for
i in
[1
, 3
, 4
, 9
, 10
]
println(
i)
end
Les conditions s'écrivent avec les mots clés if, elseif et else :
if
x >=
0
…
elseif
x <=
1
…
else
…
end
En tant que langage orienté mathématiques, Julia permet d'utiliser des comparaisons multiples : pour vérifier que x est dans l'intervalle [0, 1], on peut utiliser la condition 0 <= x <= 1. La majorité des autres langages impose de séparer la condition en deux parties : x >=0 && x <= 1.
if
0
<=
x <=
1
…
end
La syntaxe des boucles while est très similaire aux conditions, avec une condition :
while
true
…
end
L'étape d'après est d'organiser son code en fonctions, qui se déclarent avec le mot clé function, toujours suivi de parenthèses. Si la fonction prend un ou plusieurs arguments, ils sont indiqués entre les parenthèses :
function
zero()
return
0
end
function
sum(
a, b)
return
a +
b
end
La notation :: indique le type d'un argument :
function
sum(
a::
Int
, b::
Int
)
return
a +
b
end
Les structures de données immuables sont déclarées avec le mot clé struct :
struct
Point
x::
Float64
y::
Float64
end
Instancier une structure de données se fait comme un appel de fonction :
p =
Point(
0.0
, 1.0
)
Cependant, on ne peut pas modifier les instances de ces objets : pour p, on ne peut que lire les valeurs des champs x et y, pas y écrire !
println(
p.x)
# Affiche "0.0"
p.x =
1.0
# Erreur : "ERROR: setfield! immutable struct of type Point cannot be changed"
En effet, de base, Julia oriente les utilisateurs vers un style de programmation plutôt fonctionnel : si les valeurs des structures de données ne peuvent pas changer, les sources de problème dans le code sont fortement réduites.
Pour outrepasser cette limitation, il faut déclarer la structure de données comme mutable struct :
mutable struct
Point
x::
Float64
y::
Float64
end
p =
Point(
0.0
, 1.0
)
println(
p.x)
# Affiche "0.0"
p.x =
1.0
# Plus d'erreur
println(
p.x)
# Affiche "1.0"
5. Travailler avec Julia▲
On peut utiliser Julia de plusieurs manières, le langage et son environnement sont assez flexibles. La manière la plus basique de l'utiliser est en console, avec l'interface REPL (read–eval–print loop) : on tape des commandes, on récupère des résultats.
Quand on commence à avoir un peu plus de code, on finit rapidement par le mettre dans des fichiers, avec des fonctions, des structures, un petit script pour lancer le tout. Pour inclure un fichier (que ce soit depuis un fichier Julia ou l'interface REPL), on utilise la fonction include, qui exécute directement tout le code du fichier inclus :
-
depuis un fichier, on peut inclure facilement les fichiers dans le même dossier : include("autrefichier.jl") ;
-
depuis l'interface REPL, il vaut mieux inclure le chemin complet vers le fichier à exécuter : include("/usr/Thibaut/fichier.jl") ou include("C:\\Users\\Thibaut\\fichier.jl").
Cependant, cette manière de travailler n'est pas toujours pratique. Pour l'analyse de données, par exemple, une interface de type Jupyter permet de mélanger du code et des résultats (notamment des graphiques) dans un format de calepin : IJulia.jl.

Pour ceux dont le métier de base est le développement informatique, l'interface REPL n'est pas tellement rebutante. Par contre, en développant une application, on en vient parfois à ajouter, enlenver des fonctions. En incluant toujours un script (ou en important un module en cours de développement), toutes les anciennes versions des fonctions restent disponibles, à moins d'être écrasées : si on supprime une fonction de son code, elle restera disponible depuis le REPL. Du code risque donc de continuer de fonctionner, alors qu'il repose sur des fonctions qui n'existent plus… La seule manière de bien tester son code est alors de relancer Julia. Revise.jl est prévu pour résoudre ce problème : il recharge automatiquement le code modifié et supprime les fonctions qui n'existent plus. Son utilisation est très simple : après installation, il suffit d'importer le module (import Revise) ; tout code importé par la suite est alors suivi de près par Revise et rechargé si besoin. La grande limitation actuelle de Revise.jl est que le paquet ne peut rien faire quand une structure de données change : dans ce cas, on aura droit à une erreur ERROR: invalid redefinition of constant Point, la seule solution est de relancer une session Julia. (Bien évidemment, Revise.jl peut s'utiliser en parallèle avec IJulia.)
Les scientifiques pourraient profiter de DrWatson.jl, qui cherche à automatiser le chargement et la sauvegarde de données d'expériences numériques, notamment avec de grandes variations dans les paramètres des expériences. DrWatson.jl enregistre les valeurs des paramètres, gère les noms de fichiers automatiquement.
6. Et après ?▲
Bien évidemment, cette petite introduction ne montre pas tout ce que Julia peut faire ! Pour mieux organiser son code, on peut utiliser des modules, prévus pour être faciles à partager avec d'autres. Un module consiste en une série de fonctions et de structures de données, certaines étant exportées (accessibles au monde extérieur) et d'autres pas.
Julia dispose d'une série de fonctionnalités pour l'analyse de performance : principalement, @time pour mesurer le temps d'exécution d'une commande et la mémoire consommée, @profile pour montrer le temps que prend chaque partie d'une fonction.
En outre, regardez la grande quantité de modules d'extension disponibles (ainsi que la bibliothèque standard) : il est plus que probable que toutes les fonctions de base dont vous auriez besoin pour votre application soient d'ores et déjà disponibles !
Si vous souhaitez des introductions plus longues à Julia, regardez par exemple Julia Express ou ThinkJulia.jl (aussi disponible au format papier).