Oz (langage)
Modèle:Voir homonymes Modèle:Infobox Langage de programmation Oz est un langage de programmation permettant d'employer et de combiner différents paradigmes de programmation :
- fonctionnel,
- procédural et objets,
- relationnel et logique,
- contraintes,
- concurrence massive,
- distribution.
Oz fournit par défaut des variables logiques même s'il est possible d'utiliser des variables mutables. De même, l'évaluation est stricte par défaut, mais l'évaluation paresseuse est possible.
L'originalité de ce langage par rapport à d'autres supportant la programmation logique (d'une part) ou concurrente et distribuée (d'autre part, comme Erlang), est l'intégration de ces paradigmes dans un tout cohérent. Une abstraction unique en son genre est fournie par Oz : l'espace de calcul, qui permet d'encapsuler des calculs à des fins spéculatives et permet de combiner les aspects logiques/contraintes, orientation objet et mutabilité, concurrence et distribution, dans le même langage.
Oz est doté d'un ramasse-miettes et d'un système de gestion d'exceptions distribués.
Oz est implémenté par le système Mozart, fournissant un compilateur, une machine virtuelle et un environnement de développement utilisant EMACS pour la partie édition, un débogueur graphique supportant la concurrence et la distribution, un outil d'exploration d'arbres de recherche pour la programmation par contraintes, etc.
Le livre Modèle:Lang (MIT Press, 2004) utilise Oz comme langage principal pour illustrer les différents concepts de programmation. Il existe des cours universitaires de programmation en français basés sur Oz et ce livre.
Ce langage a été développé par trois écoles :
Remarque : l'origine du nom Oz vient du fameux conte pour enfants, Le Magicien d'Oz.
Fonctionnalités du langage
Structures de données
Oz est basé sur un sous-ensemble du langage, appelé "langage noyau" diposant de peu de types de données qui peuvent être étendus à d'autres, plus pratiques par le biais d'un sucre syntaxique.
Structures de données de base:
- Nombres: à virgule flottante ou entier
- Enregistrement: structure de données composée permettant de regrouper de l'information:
circle(x:0 y:1 radius:3 color:blue style:dots)
. Ici, les termes x, y, radius... sont les champs (appelés « features »), avec leurs valeurs associées. « circle » est défini comme le nom du record, son « étiquette ». - Tuple: Enregistrement avec des noms de champs entiers croissants:
circle(1:0 2:1 3:3 4:blue 5:dots)
- Liste: simple structure linéaire
<syntaxhighlight lang="text">'|'(2 '|'(4 '|'(6 '|'(8 nil)))) % Comme un enregistrement 2|(4|(6|(8|nil))) % Avec un peu de sucre syntaxique 2|4|6|8|nil % Avec plus de sucre syntaxique [2 4 6 8] % Et avec encore plus </syntaxhighlight> Ces structures de données sont des valeurs (constantes) typées dynamiquement. Les noms de variables en Oz sont écrites avec une première lettre majuscule afin de les différencier des constantes littérales<ref>Modèle:Lien web.</ref> (atomes qui eux, commencent par une minuscule).
Fonctions
Les fonctions<ref name="Programmation fonctionnelle avancée en Oz"> Modèle:Chapitre </ref> sont des valeurs de première classe, permettant la programmation d'ordre supérieur: <syntaxhighlight lang="text"> fun {Fact N}
if N =< 0 then 1 else N*{Fact N-1} end
end
fun {Comb N K}
{Fact N} div ({Fact K} * {Fact N-K}) % Pas de dépassement d'entier en Oz (tant qu'il reste de la mémoire)
end
fun {SumList List}
case List of nil then 0 [] H|T then H+{SumList T} % Détection de forme sur liste end
end </syntaxhighlight> Les fonctions peuvent utiliser à la fois des variables libres et liées. Les variables libres sont trouvées par portée lexicale<ref name="Portée"> Modèle:Chapitre </ref>.
Programmation d'ordre supérieur
Les fonctions sont comme d'autres éléments de Oz. Une fonction peut être passée comme attribut à une autre fonction ou encore être retournée par celle-ci. <syntaxhighlight lang="text"> fun {Square N} % Une simple fonction
N*N
end
fun {Map F Xs} % F est une fonction ici (utilisation de la programmation d'ordre supérieur)
case Xs of nil then nil [] X|Xr then {F X}|{Map F Xr} end
end
%Utilisation {Browse {Map Square [1 2 3]}} %Affiche [1 4 9] </syntaxhighlight>
Fonctions anonymes
Comme de nombreux autres langages fonctionnels, Oz supporte l'utilisation de fonctions anonymes (qui n'ont pas de noms) avec la programmation d'ordre supérieur. Le symbole $ est utilisé à cet usage. <syntaxhighlight lang="text"> % La fonction élevée au carré est passée comme argument anonymement {Browse {Map fun {$ N} N*N end [1 2 3]}} % Affiche [1 4 9] </syntaxhighlight>
Comme les fonctions anonymes n'ont pas de nom, il est impossible de définir récursivement des fonctions anonymes.
Procédures
Les fonctions en Oz doivent retourner une valeur comme dernière instruction de la fonction durant l'exécution. Dans l'exemple suivant, la fonction Ret retourne 5 si X > 0 et -5 dans les autres cas. <syntaxhighlight lang="text"> declare fun {Ret X}
if X > 0 then 5 else ~5 end
end </syntaxhighlight> Mais Oz fournit aussi une fonctionnalité dans le cas où nous ne souhaitons pas retourner de valeur. De telles fonctions sont appelées procédures<ref>Modèle:Lien web.</ref>. Les procédures sont définies en utilisant le mot-clef proc, comme suit: <syntaxhighlight lang="text"> declare proc {Ret X}
if X > 0 then {Browse 5} else {Browse ~5} end
end </syntaxhighlight> L'exemple ci-dessus ne retourne aucune valeur, il affiche juste 5 ou -5 dans l'interface de Oz en fonction du signe de X.
Variable dataflow et concurrence déclarative
Quand le programme rencontre une variable non liée, il attend une valeur: <syntaxhighlight lang="text"> thread
Z = X+Y % Va attendre jusqu'à ce que X et Y soient liés à une valeur {Browse Z} % Affiche la valeur de Z
end thread X = 40 end thread Y = 2 end </syntaxhighlight>
Il n'est pas possible de changer la valeur d'une variable dataflow une fois assignée (assignement unique) <syntaxhighlight lang="text"> X = 1 X = 2 % Erreur </syntaxhighlight>
Les variables dataflow rendent facile la création d'agents concurrents:
<syntaxhighlight lang="text"> fun {Ints N Max}
if N == Max then nil else {Delay 1000} N|{Ints N+1 Max} end
end
fun {Sum S Stream}
case Stream of nil then S [] H|T then S|{Sum H+S T} end
end
local X Y in
thread X = {Ints 0 1000} end % Creation d'un agent qui génère un flux thread Y = {Sum 0 X} end % Création d'un agent qui traîte le flux {Browse Y}
end </syntaxhighlight>
De par le mode de fonctionnement des variables dataflow, il est possible de placer des threads n'importe où dans le programme et il sera garanti qu'il gardera le même résultat. Ceci rend la programmation concurrente vraiment facile. Les threads sont assez légers: il est possible de faire tourner des milliers de threads à la fois <ref>http://www.mozart-oz.org/documentation/tutorial/node8.html#chapter.concurrency</ref>
Exemple : Divisions successives
Cet exemple calcule un flux de nombres premiers en utilisant l'algorithme de divisions successives en créant des agents qui filtrent les nombres non premiers. <syntaxhighlight lang="text"> fun {Sieve Xs}
case Xs of nil then nil [] X|Xr then Ys in thread Ys = {Filter Xr fun {$ Y} Y mod X \= 0 end} end X|{Sieve Ys} end
end </syntaxhighlight>
Paresse
Oz utilise l'évaluation stricte mais aussi l'évaluation paresseuse<ref name="Lazy Programming"> Modèle:Chapitre </ref> si possible: <syntaxhighlight lang="text"> fun lazy {Fact N}
if N =< 0 then 1 else N*{Fact N-1} end
end local X Y in
X = {Fact 100} Y = X + 1 % On a besoin de la valeur de X et Fact est calculée
end </syntaxhighlight>
L'évaluation paresseuse donne la possibilité de stocker presque une infinité de structures de données en Oz. Le pouvoir de l'évaluation paresseuse peut se voir par le biais du fragment de code suivant:
<syntaxhighlight lang="text"> declare fun lazy {Merge Xs Ys}
case Xs#Ys of (X|Xr)#(Y|Yr) then if X < Y then X|{Merge Xr Ys} elseif X>Y then Y|{Merge Xs Yr} else X|{Merge Xr Yr} end end
end
fun lazy {Times N Xs}
case Xs of nil then nil [] X|Xr then N*X|{Times N Xr} end
end
declare H H = 1 | {Merge {Times 2 H} {Merge {Times 3 H} {Times 5 H}}} {Browse {List.take H 6}} </syntaxhighlight> Le code ci-dessus calcule de manière élégante tous les nombres premiers réguliers<ref name="Hamming Numbers"> Modèle:Chapitre </ref> dans une liste infinie. Les nombres réels ne sont calculés que si on les utilise.
Concurrence par envoi de messages
Le modèle déclaratif concurrent peut être étendu par l'envoi de message à l'aide d'une sémantique simple: <syntaxhighlight lang="text"> declare local Stream Port in
Port = {NewPort Stream} {Send Port 1} % Stream is now 1|_ ('_' indique une variable non liée) {Send Port 2} % Stream is now 1|2|_ ... {Send Port n} % Stream is now 1|2| .. |n|_
end </syntaxhighlight>
Par un port et un thread, le programmeur peut définir des agents asynchrones: <syntaxhighlight lang="text"> fun {NewAgent Init Fun}
Msg Out in thread {FoldL Msg Fun Init Out} end {NewPort Msg}
end </syntaxhighlight>
État et objets
Il est encore possible d'étendre le model déclaratif afin de supporter l'état et la programmation orientée objet par une sémantique très simple; nous créons une nouvelle strucure mutable, les cellules (Cells). <syntaxhighlight lang="text"> local A X in
A = {NewCell 0} A := 1 % Changes la valeur de A à 1 X = @A % @ Est utilisé pour accéder à la valeur de A
end </syntaxhighlight>
Avec cette sémantique très simple, Oz peut supporter tout le paradigme orienté-objet. Un peu de sucre syntaxique permet d'intégrer très facilement l'OOP comme suit: <syntaxhighlight lang="text"> class Counter
attr val meth init(Value) val:=Value end meth browse {Browse @val} end meth inc(Value) val :=@val+Value end
end
local C in
C = {New Counter init(0)} {C inc(6)} {C browse}
end </syntaxhighlight>
Notes et références
Liens externes
- {{#invoke:Langue|indicationDeLangue}} Modèle:Lang (mozart2.org)