Oz (langage)

Oz
Date de première version 1995
Paradigmes objet, impératif, concurrent, fonctionnel, logique, par contraintes, distribué
Auteurs Gert Smolka, Christian Schulte, Hugo Sky, Peter Van Roy
Développeurs auteurs et d'autres contributeurs
Dernière version 2.0.1[1] ()
Typage Fort, dynamique
Influencé par Prolog, CC, AKL, Lisp, Haskell, Erlang
A influencé une extension pour la programmation logique et par contraintes, dans PyPy
Implémentations Mozart
Écrit en C++Voir et modifier les données sur Wikidata
Système d'exploitation Multiplate-forme
Licences de type BSD
Site web [1]

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 langage

Structures de données

Oz 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 :

  • 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
'|'(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).

Fonctions

Les 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é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.

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 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.

% 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é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.

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éclarative

Quand 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 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.

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

Paresse

Oz 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 messages

Le 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 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).

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

  1. (en) « Releases · mozart/mozart2 », sur GitHub (consulté le ).
  2. « Mozart2 version 2.0.1 Latest », (consulté le )
  3. « 3 Basics », sur mozart.github.io (consulté le ).
  4. Leif Grönqvist, « Higher Order Functions », dans Advanced Functional Programming in Oz (lire en ligne)
  5. Robert Gentleman et Ross Ihaka, « Lexical Scope in Statistical Computing », dans Journal of Computational and Graphical Statistics, vol. 9, (lire en ligne), chap. 3, Systems and Languages, p. 491–508
  6. « 5 Basic Control Structures », sur mozart.github.io (consulté le ).
  7. http://www.mozart-oz.org/documentation/tutorial/node8.html#chapter.concurrency
  8. Paul Hudak, « Conception, evolution, and application of functional programming languages », dans ACM Computing Surveys (CSUR), vol. 21, p. 359–411
  9. Rao, AC and Varada Raju, D, « Application of the Hamming number technique to detect isomorphism among kinematic chains and inversions », dans Mechanism and Machine theory, vol. 26, , p. 55–75

Liens externes