Masquotte de Caml Mocac: un générateur de modules pour les types à relations

Version 0.3.0

Où trouver le logiciel ?

Les sources de mocac sont ici.

Comment l'installer ?

Voir le fichier INSTALL dans le répertoire source.
Pour Windows : voir le fichier INSTALL.win dans le répertoire source.

De quoi s'agit-il ?

Mocac est un générateur de fonctions de construction pour des types de données concrets Caml avec des invariants algébriques et partage maximal. Les invariants algébriques sont spécifiés à l'aide de mot-clés dénotant des théories équationnelles comme la commutativité et l'associativité. Les fonctions de constructions générées par Mocac permette à chaque classe d'équivalence d'être représentée par une unique valeur.

Principe

Mocac lit un fichier .mlm et produit un module Objective Caml (fichier d'interface + fichier d'implementation).
Un fichier .mlm est semblable à un fichier d'interface .mli habituel: il doit définir un type (privé), avec la possibilité supplémentaire de déclarer des relations algébriques qui sont vérifiées par les constructeurs.
Mocac génère alors les fonctions de constructions pour les constructeurs, de telle sorte que les relations sont effectivement vérifiées pour toutes les valeurs du type défini.

Les définitions de type pour mocac ont la même syntaxe que celles d'Objective Caml avec en plus les relations algébriques associées aux constructeurs définis dans le type entre les mots clés begin et end.

Bénéfice supplémentaire, vous pouvez obtenir un partage maximal des données construites par les fonctions de construction en utilisant l'option spéciale --sharing du compilateur mocac.

Comment s'en servir ?

Il suffit d'appeler mocac avec pour argument votre fichier .mlm.
Sous Windows : appeler sh mocac avec pour argument votre fichier .mlm.

Grammaire

Moca étend la grammaire d'OCaml de la manière suivante:

constr-decl ::= constr-name [annotation]
  constr-name of typexpr [annotation]
annotation ::= begin {relation}+ end
side ::= left
  right
invopp ::= inverse
  opposite
relation ::= commutative
  associative
  involutive
  idempotent [side]
  neutral [side] ( constr-name )
  nilpotent ( constr-name )
  invopp [side] ( constr-name [, constr-name] )
  inverse neutral ( constr-name [, constr-name] )
  distributive [side] ( constr-name )
  unary distributive ( constr-name [, constr-name] )
  distributive invopp [side] ( constr-name )
  absorbent [side] ( constr-name )
  absorbing [side] ( constr-name )
  rule pattern -> pattern

Sémantique

Nous donnons la thérie équationnelle correspondant à chaque mot-clé et les propriétés des représentants des classes d'équivalence générés par Mocac.
Si C est commutative,
alors C(x,y)=C(y,x) et, pour chaque valeur filtrant C(x,y), nous avons Pervasives.compare x y < 0.
Si C est associative,
alors C(C(x,y),z)=C(x,C(y,z)) et aucune valeur n'est filtrée C(C(x,y),z).
Si C est involutive,
alors C(C(x))=x et aucune valeur n'est filtrée C(C(x)).
idempotent
est la conjonction de idempotent left et idempotent right.
Si C est idempotent left,
alors C(x,C(x,y))=C(x,y) et aucune valeur n'est filtrée par C(x,C(x,y)).
Si C est idempotent right,
alors C(C(x,y),y)=C(x,y) et aucune valeur n'est filtrée par C(C(x,y),y).
neutral (D)
est the conjunction of neutral left (D) et neutral right (D).
Si C est neutral left (D),
alors C(D,x)=x et aucune valeur n'est filtrée par C(D,x).
Si C est neutral right (D),
alors C(x,D)=x et aucune valeur n'est filtrée par C(x,D).
Si C est nilpotent (A),
alors C(C(x))=A et aucune valeur n'est filtrée par C(C(x)).
inverse (I,E)
est the conjunction of inverse left (I,E) et inverse right (I,E).
Si C est inverse left (I,E),
alors C(I(x),x)=E et aucune valeur n'est filtrée par C(I(x),x).
Si C est inverse right (I,E),
alors C(x,I(x))=E et aucune valeur n'est filtrée par C(x,I(x)).
Si C est C est neutral [side] (E),
alors inverse [side'] (I) est equivalent to inverse [side'] (I,E).
Si C est inverse [side] (I,E) and absorbent [side'] (A),
alors la fonction de construction associée à C lève l'exception (Failure "Division by Absorbent") quand un des arguments est A.
Si I est inverse neutral (E),
alors I(E)=E et no valeur n'est filtrée par I(E).
Si I est inverse neutral (E,A),
alors I(E)=A et no valeur n'est filtrée par I(E).
distributive (D)
est the conjunction of distributive left (D) et distributive right (D).
Si C est distributive left (D),
alors C(D(x,y),z)=D(C(x,z),C(y,z)) et aucune valeur n'est filtrée par C(D(x,y),z).
Si C est distributive right (D),
alors C(z,D(x,y))=D(C(z,x),C(z,y)) et aucune valeur n'est filtrée par C(z,D(x,y)).
Si I est unary distributive (C,D),
alors I(C(x,y))=D(I(y),I(x)) et aucune valeur n'est filtrée par I(C(x,y)).
unary distributive (C)
est équivalent à unary distributive (C,C).
distributive inverse (I) est la conjonction de distributive inverse left (I) et distributive inverse right (I).
Si C est distributive inverse left (I),
alors C(I(x),y)=I(C(x,y)) et aucune valeur n'est filtrée par C(I(x),y).
Si C est distributive inverse right (I),
alors C(x,I(y))=I(C(x,y)) et aucune valeur n'est filtrée par C(x,I(y)).
absorbent (A)
est la conjonction de absorbent left (A) et absorbent right (A).
Si C est absorbent left (A),
alors C(A,x)=A et aucune valeur n'est filtrée par C(A,x).
Si C est absorbent right (A),
alors C(x,A)=A et aucune valeur n'est filtrée par C(x,A).
absorbing (D)
est la conjunction de absorbing left (D) et absorbing right (D).
Si C est absorbing left (D),
alors C(D(x,y),y)=y et aucune valeur n'est filtrée par C(D(x,y),y).
Si C est absorbing right (D),
alors C(x,D(x,y))=x et aucune valeur n'est filtrée par C(x,D(x,y)).
Si C has rule l -> r,
alors C(l)=r et aucune valeur n'est filtrée par C(l). Cette annotation est fournie pour les utilisateurs experts seulement quand les annotations prédéfinies précédentes sont insuffisantes. Dans le code généré, les constructeurs dans r sont remplacés par des appels aux fonctions de construction correspondantes, et les simplifications induites par ces annotations sont appliquées autant que possible et en priorité. Quand il y a une telle annotation, le code généré n'est plus garanti d'être correct ou même de terminer.

Exemples

Voici une définition pour un type de données qui représente les valeurs d'un groupe additif. Le groupe comporte une opération binaire Add, un élément neutre Zero, un opérateur unaire pour l'opposé Opp, et un générateur One:

type t = private
   | Zero
   | One
   | Opp of t
   | Add of t * t
     begin
       associative
       commutative
       neutral (Zero)
       opposite (Opp)
     end
;;

Les propriétés algébriques des opérateurs du groupe sont ici portées par l'opération binaire Add. Les mots clés associative, commutative, neutral et opposite sont spécifiques à Moca et confèrent les propriétés habituelles correspondantes au constructeur Add.

Si l'on suppose que ce code est dans le fichier group.mlm, alors la commande mocac group.mlm génère le module Group sous la forme des deux fichiers group.mli et group.ml.

Le fichier d'interface group.mli déclare le type privé t qui est le support des valeurs du groupe et déclare la signature des fonctions de construction associées aux constructeurs:

type t = private
   | Zero
   | One
   | Opp of t
   | Add of t * t
;;
val add : t * t -> t;;
val one : t;;
val opp : t -> t;;
val zero : t;;

Le fichier d'implémentation group.ml définit le type t et les fonctions de construction correspondantes. Son contenu est équivalent à:

type t =
   | Zero
   | One
   | Opp of t
   | Add of t * t
;;

let rec add z =
  match z with
  | (Zero, y) -> y
  | (x, Zero) -> x
  | (Add (x, y), z) -> add (x, add (y, z))
  | (Opp x, y) -> insert_inv_add x y
  | (x, Opp y) -> insert_inv_add y x
  | (x, y) -> insert_inv_add (opp x) y

and delete_add x u =
  match u with
  | Add (y, _) when x < y -> raise Not_found
  | Add (y, t) when x = y -> t
  | Add (y, t) -> Add (y, delete_add x t)
  | _ when u = x -> Zero
  | _ -> raise Not_found

and insert_inv_add x u =
  try delete_add x u with
  | Not_found -> insert_add (opp x) u

and insert_add x u =
  match u with
  | Add (y, _) when x < y -> Add (x, u)
  | Add (y, t) -> Add (y, insert_add x t)
  | _ when x > u -> Add (u, x)
  | _ -> Add (x, u)

and one = One

and opp x =
  match x with
  | Zero -> Zero
  | Opp x -> x
  | Add (x, y) -> add (opp x, opp y)
  | _ -> Opp x

and zero = Zero;;

Les valeurs du type t sont maintenant toutes correctement normalisées selon les règles des groupes (autrement dit, il n'existe pas de valeur du type t qui ne soit normalisée). Par exemple:

# add (one, add (zero, opp one));;
- : t = Zero

Le répertoire examples de la distribution contient de nombreux autres exemples de structures de données traitées par mocac.

Bibliographie

On the implementation of construction functions for non-free concrete data types, F. Blanqui, T. Hardin and P. Weis, ESOP'07.

Contact

Frédéric Blanqui ou Pierre Weis

Fichier créé le 11 avril 2005.


Dernière modification le vendredi 27 avril 2007.
Copyright © 2005 - 2007 INRIA, tous droits réservés.