contrainte de type par signature
This commit is contained in:
parent
98dd6eea17
commit
07b57d4515
|
@ -37,8 +37,8 @@ def setup(app):
|
||||||
app.add_config_value('correction', False, 'env')
|
app.add_config_value('correction', False, 'env')
|
||||||
app.add_config_value('exercice', False, 'env')
|
app.add_config_value('exercice', False, 'env')
|
||||||
|
|
||||||
exercice = False
|
exercice = True
|
||||||
correction = False
|
correction = True
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
|
|
@ -7,17 +7,28 @@ définies.
|
||||||
|
|
||||||
Ces modules doivent être aussi indépendants que possible.
|
Ces modules doivent être aussi indépendants que possible.
|
||||||
|
|
||||||
module
|
.. glossary::
|
||||||
|
|
||||||
ensemble de ressources liées sémantiquement
|
module
|
||||||
|
|
||||||
interface
|
ensemble de ressources liées sémantiquement
|
||||||
|
|
||||||
mode d’emploi du module, avec en plus un principe de masquage
|
interface
|
||||||
des informations (partie publique, partie secrète)
|
|
||||||
|
mode d’emploi du module, avec en plus un principe de masquage
|
||||||
|
des informations (partie publique, partie secrète)
|
||||||
|
|
||||||
|
|
||||||
Signatures, type abstrait et langage de modules : la programmation modulaire
|
signature
|
||||||
|
|
||||||
|
suite de déclarations, types, exceptions, valeurs, modules, etc.
|
||||||
|
|
||||||
|
implantation
|
||||||
|
|
||||||
|
suite de définitions, qui doit comporter tout ce qui est requis par la
|
||||||
|
signature
|
||||||
|
|
||||||
|
Type abstrait et langage de modules : la programmation modulaire
|
||||||
permet d'aller très loin dans la programmation structurée.
|
permet d'aller très loin dans la programmation structurée.
|
||||||
|
|
||||||
|
|
||||||
|
@ -188,7 +199,7 @@ Le langages des modules
|
||||||
|
|
||||||
(* implémentation du module *)
|
(* implémentation du module *)
|
||||||
module Pile : PILE = (* le module est restreint
|
module Pile : PILE = (* le module est restreint
|
||||||
par la signature PILE *)
|
par la signature PILE *)
|
||||||
struct
|
struct
|
||||||
type ’a t = ’a list ref
|
type ’a t = ’a list ref
|
||||||
let create () = ref []
|
let create () = ref []
|
||||||
|
@ -216,41 +227,41 @@ En dehors du module, on accède à ses composants grâce à la notation pointée
|
||||||
let p = Pile.create()
|
let p = Pile.create()
|
||||||
Pile.push 45 p
|
Pile.push 45 p
|
||||||
|
|
||||||
Les foncteurs
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Si un langage possède un langage de modules, on peut aller plus loin : on peut
|
**Signature inférée** : Lorsque la déclaration d'interface n'existe pas.
|
||||||
considérer un module comme étant une expression de base du langage.
|
|
||||||
|
|
||||||
- La signature d'un module peut être considérée comme le type du module
|
|
||||||
- La structure du module peut être considéré comme sa valeur
|
|
||||||
|
|
||||||
Quel est l'intérêt ? On peut alors définir des **foncteurs**.
|
|
||||||
|
|
||||||
foncteur
|
|
||||||
|
|
||||||
"fonction" d'une structure vers une autre structure.
|
|
||||||
On peut ainsi paramétrer un module par un autre module.
|
|
||||||
|
|
||||||
.. code-block:: ocaml
|
|
||||||
|
|
||||||
module Nom (M1 :S1 ) (M2 :S2 ) (M3 :S3 ) ... =
|
|
||||||
struct
|
|
||||||
...
|
|
||||||
end
|
|
||||||
|
|
||||||
On applique un foncteur à des paramètres modules, pour
|
|
||||||
obtenir un nouveau module :
|
|
||||||
|
|
||||||
.. code-block:: ocaml
|
|
||||||
|
|
||||||
module M = F (Titi) (Toto)
|
|
||||||
|
|
||||||
|
|
||||||
Contrainte de type par signature
|
Contrainte de type par signature
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
::
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
module M =
|
||||||
|
struct
|
||||||
|
type t = int * int * int
|
||||||
|
let make d m y = d,m,y
|
||||||
|
end
|
||||||
|
|
||||||
|
module M :
|
||||||
|
sig type t = int * int * int
|
||||||
|
val make : ’a -> ’b -> ’c -> ’a * ’b * ’c end
|
||||||
|
|
||||||
|
.. ifconfig:: exercice
|
||||||
|
|
||||||
|
ici la signature inférée est du type le plus général.
|
||||||
|
Cela peut poser des difficultés. Lesquelles ?
|
||||||
|
|
||||||
|
.. ifconfig:: correction
|
||||||
|
|
||||||
|
une incohérence entre type attendu et type obtenu
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
# let d = M.make 52 24 137 ;;
|
||||||
|
val d : int * int * int = (52, 24, 137)
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
module M =
|
module M =
|
||||||
struct
|
struct
|
||||||
|
@ -270,6 +281,58 @@ Contrainte de type par signature
|
||||||
|
|
||||||
MS.make 5 1 2 ;;
|
MS.make 5 1 2 ;;
|
||||||
|
|
||||||
|
|
||||||
|
autre exemple
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
module type DATE =
|
||||||
|
sig
|
||||||
|
type t = int * int * int
|
||||||
|
val mmax : int -> int -> int
|
||||||
|
val make : int -> int -> int -> t
|
||||||
|
val get_day : t -> int
|
||||||
|
val get_month : t -> int
|
||||||
|
val get_year : t -> int
|
||||||
|
end
|
||||||
|
|
||||||
|
on est alors obligé de contrôler la vraissemblance des valeurs dans chaque fonction du module, sinon le contrôle n'est pas maîtrisé...
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
M.get_month (23,45,67);;
|
||||||
|
- : int = 45
|
||||||
|
|
||||||
|
Le problème vient du fait que l’implantation du type Date.t est **publique**.
|
||||||
|
|
||||||
|
Le Type abstrait de données permet de masquer l’implantation du type.
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
module type DATE = sig
|
||||||
|
type t
|
||||||
|
val mmax : int -> int -> int
|
||||||
|
val make : int -> int -> int -> t
|
||||||
|
val get_day : t -> int
|
||||||
|
val get_month : t -> int
|
||||||
|
val get_year : t -> int
|
||||||
|
end
|
||||||
|
|
||||||
|
usage::
|
||||||
|
|
||||||
|
# module Date = (M:DATE) ;;
|
||||||
|
module Date : DATE
|
||||||
|
|
||||||
|
Le contrôle est maîtrisé::
|
||||||
|
|
||||||
|
Date.get_month (23,45,67);;
|
||||||
|
This expression has type int * int * int but is here used with type Date.t
|
||||||
|
|
||||||
|
Si (M:S) alors la structure M est une instance de la signa-
|
||||||
|
ture S
|
||||||
|
|
||||||
|
|
||||||
Type et signature
|
Type et signature
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -311,3 +374,143 @@ Module auquel on impose une signature
|
||||||
end ;;
|
end ;;
|
||||||
|
|
||||||
module date = (MR:DATE) ;;
|
module date = (MR:DATE) ;;
|
||||||
|
|
||||||
|
|
||||||
|
Structure et signature
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Une structure peut avoir plusieurs signatures
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
# module Cpt =
|
||||||
|
struct
|
||||||
|
let x = ref 0
|
||||||
|
let reset () = x := 0
|
||||||
|
let next () = incr x; !x
|
||||||
|
end
|
||||||
|
|
||||||
|
.. ifconfig:: exercice
|
||||||
|
|
||||||
|
Créer deux modules n’ayant pas les mêmes droits sans toucher à
|
||||||
|
l’implantation.
|
||||||
|
|
||||||
|
.. ifconfig:: correction
|
||||||
|
|
||||||
|
une vue administrateur
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
module type ADM =
|
||||||
|
sig
|
||||||
|
val reset : unit -> unit
|
||||||
|
val next : unit -> int
|
||||||
|
end
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
module Adm = (Cpt:ADM)
|
||||||
|
# (* le compteur lui-même est invisible *)
|
||||||
|
# Adm.x;;
|
||||||
|
Unbound value Adm.x
|
||||||
|
|
||||||
|
une vue utilisateur
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
module type USR =
|
||||||
|
sig
|
||||||
|
val next : unit -> int
|
||||||
|
end
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# module Usr = (Cpt:USR)
|
||||||
|
# Usr.next();;
|
||||||
|
- : int = 1
|
||||||
|
# Usr.reset();;
|
||||||
|
Unbound value Usr.reset
|
||||||
|
|
||||||
|
Contrainte partage du code (et du compteur) : impossible de construire deux composants administrateurs et utilisateurs autonomes.
|
||||||
|
|
||||||
|
- une unité qui contient les trois modules
|
||||||
|
- un composant qui publie les deux modules mais pas le module commun (un package)
|
||||||
|
|
||||||
|
L'héritage par inclusion
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
# module Cpt2 = struct
|
||||||
|
include Cpt
|
||||||
|
let get() = !x
|
||||||
|
let next() = x := 2 * !x; !x
|
||||||
|
end ;;
|
||||||
|
module Cpt2 :
|
||||||
|
sig
|
||||||
|
val x : int ref
|
||||||
|
val reset : unit -> unit
|
||||||
|
val get : unit -> int
|
||||||
|
val next : unit -> int
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Les foncteurs
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Si un langage possède un langage de modules, on peut aller plus loin : on peut
|
||||||
|
considérer un module comme étant une expression de base du langage.
|
||||||
|
|
||||||
|
- La signature d'un module peut être considérée comme le type du module
|
||||||
|
- La structure du module peut être considéré comme sa valeur
|
||||||
|
|
||||||
|
Quel est l'intérêt ? On peut alors définir des **foncteurs**.
|
||||||
|
|
||||||
|
foncteur
|
||||||
|
|
||||||
|
"fonction" d'une structure vers une autre structure.
|
||||||
|
On peut ainsi paramétrer un module par un autre module.
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
module Nom (M1 :S1 ) (M2 :S2 ) (M3 :S3 ) ... =
|
||||||
|
struct
|
||||||
|
...
|
||||||
|
end
|
||||||
|
|
||||||
|
On applique un foncteur à des paramètres modules, pour
|
||||||
|
obtenir un nouveau module :
|
||||||
|
|
||||||
|
.. code-block:: ocaml
|
||||||
|
|
||||||
|
module M = F (Titi) (Toto)
|
||||||
|
|
||||||
|
|
||||||
|
exemple, le module Set::
|
||||||
|
|
||||||
|
|
||||||
|
module OrderedInt =
|
||||||
|
struct type t=int let compare = compare end ;;
|
||||||
|
module OrderedInt : sig type t = int val compare : ’a -> ’a -> int end
|
||||||
|
# module IntSet = Set.Make(OrderedInt)
|
||||||
|
module IntSet :
|
||||||
|
sig
|
||||||
|
type elt = OrderedInt.t
|
||||||
|
type t = Set.Make(OrderedInt).t
|
||||||
|
val empty : t
|
||||||
|
|
||||||
|
Ou, plus court::
|
||||||
|
|
||||||
|
|
||||||
|
module IntSet = Set.Make(struct type t=int let compare = compare end)
|
||||||
|
|
||||||
|
|
||||||
|
Le parallèle peut se faire entre les classes et les modules.
|
||||||
|
|
||||||
|
- module : Encapsulation données/traitements
|
||||||
|
- classes : différenciation données/traitements et possibilité de création de plusieurs instances
|
||||||
|
|
||||||
|
|
||||||
|
classes et types, inférences de types de classes
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue