Oz (langage)
Oz est un langage de programmation permettant d'employer et de combiner différents paradigmes de programmation :
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 Concepts, Techniques, and Models of Computer Programming (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 langageStructures de donnéesOz est basé sur un sous-ensemble du langage, appelé « langage noyau » disposant 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 :
'|'(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
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[3] (atomes qui eux, commencent par une minuscule). FonctionsLes fonctions[4] sont des valeurs de première classe, permettant la programmation d'ordre supérieur: 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
Les fonctions peuvent utiliser à la fois des variables libres et liées. Les variables libres sont trouvées par portée lexicale[5]. Programmation d'ordre supérieurLes 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. 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]
Fonctions anonymesComme 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. % 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]
Comme les fonctions anonymes n'ont pas de nom, il est impossible de définir récursivement des fonctions anonymes. ProcéduresLes 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. declare
fun {Ret X}
if X > 0 then 5 else ~5 end
end
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[6]. Les procédures sont définies en utilisant le mot-clef proc, comme suit: declare
proc {Ret X}
if X > 0 then {Browse 5} else {Browse ~5} end
end
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éclarativeQuand le programme rencontre une variable non liée, il attend une valeur: 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
Il n'est pas possible de changer la valeur d'une variable dataflow une fois assignée (assignement unique) X = 1
X = 2 % Erreur
Les variables dataflow rendent facile la création d'agents concurrents: 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
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 [7] Exemple : Divisions successivesCet 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. 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
ParesseOz utilise l'évaluation stricte mais aussi l'évaluation paresseuse[8] si possible: 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
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: 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}}
Le code ci-dessus calcule de manière élégante tous les nombres premiers réguliers[9] dans une liste infinie. Les nombres réels ne sont calculés que si on les utilise. Concurrence par envoi de messagesLe modèle déclaratif concurrent peut être étendu par l'envoi de message à l'aide d'une sémantique simple: 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
Par un port et un thread, le programmeur peut définir des agents asynchrones: fun {NewAgent Init Fun}
Msg Out in
thread {FoldL Msg Fun Init Out} end
{NewPort Msg}
end
État et objetsIl 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). 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
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: 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
Notes et références
Liens externes
|