• Nenhum resultado encontrado

précise. Pour indiquer au système que le module MutableSetByHashtbl est un raffi- nement du module MutableSet, l’idée est de clore le module MutableSetByHashtbl par une déclaration de clonage

clone MutableSet with type t = t, val create = create, ...

L’instanciation d’un foncteur est exprimée alors de la même manière, en substituant lors du clonage les modules de paramètres par leurs implémentations. Pour obtenir le code du parcours de graphe avec les tables de hachages comme implémentation des ensembles mutables, il faudra écrire

module DFSWithHashtbl

clone DFS with MutableSet = MutableSetByHashtbl end

La figure 4.4 résume brièvement ces différences de conception entre le système de modules OCamlet celui de Why3.

Concept Interface

Foncteur

Implémentation

Instanciation

OCaml

module type S = sig end...

module F(X:S) = struct end...

module (A:S) = struct end...

module Main = F(A)

Why3

module S = end...

module F use S as X end...

module A ...

clone S with ...

end

module Main

clone F with X = A end

Figure 4.4 – Comparaison des systèmes de modules OCamletWhy3.

Dès l’introduction, nous avons séparé les conditions de vérification qu’impose le principe de substitution de Liksov, en deux catégories : la première catégorie concerne tout ce qui relève du contenu logique, préservation des invariants, le sous-typage des contrats de fonctions, etc ; la deuxième catégorie concerne tout ce qui relève des pro- priétés de sûreté assurés par le système du typage. Concrètement, dans ce chapitre,

nous avons considéré comme une telle propriété le contrôle statique des alias. Il est intéressant de remarquer que l’égalité polymorphe constitue un autre cas subtil pour le raffinement où l’implémentation peut casser la barrière d’abstraction, comme l’ex- plique Appel dans [2]. En effet, l’utilisation d’égalité polymorphe, lorsqu’elle est appli- quée à des types que l’on ne peut par comparer structurellement, produit une erreur à l’exécution, sans que cela soit détecté par le typage. Illustrons ce propos en OCaml.

Déclarons une interface A de la manière suivante : module type A = sig

type t ’a

val create : ’a æ ’a t

val equal : ’a t æ ’a t æ bool end

Supposons maintenant que l’on a écrit un client très simple : module Client (X: A) = struct

let v = X.create (fun x æ x) let b = X.equal v v

end

Ensuite, donnons le module d’implémentation de l’interface A suivante : module B : A = struct

type ’a t = ’a option exception Empty

let get x = match x with

| None æ raise Empty

| Some x æ x

let equal x y = get x = get y let create x = Some x

end

Comme on le voit, ci-dessus, la fonction d’égalitéequalest réalisée à l’aide de l’égalité polymorphe. Maintenant, on peut donc relier le code du client avec l’implémentation :

module ClientB = Client(B)

Or, bien que le typage accepte cette instanciation, elle produit une erreur, puisque l’égalité structurelle ne s’applique pas aux fonctions : l’égalité polymorphe peut donc briser la barrière d’abstraction. Bien que nous avons écrit l’exemple enOCaml, l’exemple reste pertinent pour nous : nous devrions étudier comment restreindre l’usage de l’éga- lité polymorphe dans le même esprit que nous avons étudié comme restreindre l’usage des régions introduites par le raffinement.

Bibliographie

[1] Jean-Raymond Abrial, The B-book, assigning programs to meaning, Cambridge University Press, 1996.

[2] Andrew W. Appel,A critique of Standard ML., Tech. Report CS-TR-364-92, Prin- ceton University, 1992.

[3] Cliff B. Jones, Systematic software development using VDM (2nd ed.), Prentice- Hall, 1990.

[4] Jason Koenig and K. Rustan M. Leino, Programming language features for refine- ment, Proceedings 17th International Workshop on Refinement, Refine@FM 2015, Oslo, Norway, 22nd June 2015., 2015, pp. 87–106.

[5] K. Rustan M. Leino and Valentin Wüstholz, The Dafny integrated development environment, Proceedings 1st Workshop on Formal Integrated Development Envi- ronment, F-IDE 2014, Grenoble, France, April 6, 2014. (Catherine Dubois, Dimitra Giannakopoulou, and Dominique Méry, eds.), Electronic Proceedings in Theoreti- cal Computer Science, vol. 149, 2014, pp. 3–15.

[6] Xavier Leroy, A modular module system, Journal of Functional Programming 10 (2000), no. 3, 269–303.

[7] Barbara H. Liskov and Jeannette M. Wing,A behavioral notion of subtyping, ACM Transactions on Programming Languages and Systems16(1994), no. 6, 1811–1841.

[8] Jim Woodcock and Jim Davies, Using Z : Specification, refinement, and proof, Prentice-Hall, Inc., 1996.

5

Dans cette thèse nous avons exploré des solutions qu’une approche à base de sys- tèmes de types peut apporter à la vérification des programmes. Concrètement, nous avons étudié trois aspects du langage de programmationWhyML, à savoir lecode fan- tôme, le contrôle statique des alias et le raffinement des données. Pour chacune de ces trois notions, nous avons d’abord présenté, d’une manière informelle, les idées clés de l’approche, puis nous l’avons formalisée afin de montrer sa correction.

5.1 Choix et limitations

Précisons que notre but était de modéliser non pas le langage WhyML dans son intégralité, mais plutôt ces trois aspects pris séparément. Avec cet objectif en ligne de mire, nous avons fait le choix de circonscrire, pour chacune des trois notions, un fragment de WhyML que nous avons jugé pertinent au sens que nous expliquons dans les paragraphes ci-dessous.

D’une part, les fragments formalisés devaient refléter aussi bien les idées clés que les points subtils des solutions implantées dans l’outil Why3. Ainsi, pour le code fantôme, le formalisme GhostML reflète l’expressivité avec laquelle WhyML établit où le code fantôme a le droit d’apparaître et ce qui peut apparaître dans le code fantôme. De même, dans le chapitre sur les régions, le formalisme RegML contient l’essentiel du contrôle statique des alias tel qu’il est assuré par le typage en WhyML.

D’autre part, ces fragments devaient être raisonnablement petits. Ce dernier point mérite une discussion à part sur la manière dont on formalise aujourd’hui les concepts des langages de programmation. En effet, les formalisations de ce genre se font aujour- d’hui de plus en plus non sur papier, mais d’une manière mécanisée, typiquement à l’aide d’un assistant de preuve comme Coq ouIsabelle/HOL. Non seulement la preuve mécanisée apporte un degré de confiance inégalé, mais elle devient inévitable lorsque l’on formalise un langage de programmation dans son intégralité. En effet, le nombre de cas à considérer, y compris des cas de base qui demandent un raisonnement simple et répétitif, devient alors tellement important qu’il n’est pratiquement plus possible de les écrire sur papier et encore moins de convaincre le lecteur que les raisonnements sont corrects. Néanmoins, lorsque l’on formalise un aspect du langage en particulier, il est souvent possible d’en isoler un fragment qui, tout en restant pertinent, est suffisamment

139

petit pour qu’il puisse être formalisé sur papier d’une façon claire et compréhensible et que les raisonnements menés convainquent le lecteur. Par ailleurs, alors qu’une preuve mécanisée offre de nombreux avantages par rapport à une preuve papier, elle requiert en contrepartie un temps de développement non négligeable et supérieur à celui que demande une preuve sur papier. Notre choix de présenter les formalismes dans un style

« papier-crayon » nous paraît donc argumenté, d’une part, par l’adéquation des frag- ments choisis, et d’autre part, par les contraintes du temps pour mener notre travail à son terme.

Enfin, précisons que les simplifications qui éloignent les fragments formalisés de ce qui est implanté dans WhyML pour le code fantôme et les régions sont discutées dans les deux cas dans la sous-section « Implémentation » des chapitres correspondants où nous expliquons comment le formalisme pourrait être étendu pour tenir compte des traits du langage tels que le polymorphisme des types, les exceptions, les types récursifs, etc.