2017-08-28 17:36:36 +02:00
|
|
|
|
La programmation modulaire
|
|
|
|
|
===========================
|
|
|
|
|
|
|
|
|
|
Il s'agit de décomposer un grand programme en
|
|
|
|
|
morceaux (**modules**) connectés entre eux par des **interfaces** bien
|
|
|
|
|
définies.
|
|
|
|
|
|
|
|
|
|
Ces modules doivent être aussi indépendants que possible.
|
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
.. glossary::
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
module
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
ensemble de ressources liées sémantiquement
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
interface
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
mode d’emploi du module, avec en plus un principe de masquage
|
|
|
|
|
des informations (partie publique, partie secrète)
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
|
|
|
|
|
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
|
2017-08-28 17:36:36 +02:00
|
|
|
|
permet d'aller très loin dans la programmation structurée.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Définir des fonctions dans un fichier séparé
|
|
|
|
|
--------------------------------------------
|
|
|
|
|
|
|
|
|
|
Les fonctions peuvent être définies dans un fichier et le programme dans un
|
|
|
|
|
autre fichier séparé. Dans ce cas, pour pouvoir être exécuté directement avec
|
|
|
|
|
la commande python `nomfichierprogramme.py`, le fichier du programme doit
|
|
|
|
|
importer d’abord les fonctions du fichier dans lequel les fonctions sont
|
|
|
|
|
définies.
|
|
|
|
|
|
|
|
|
|
1. Fichier de fonctions
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
# Fichier foncmaxliste.py
|
|
|
|
|
# Recherche le premier élément maximal dans une liste ou
|
|
|
|
|
#dans une chaine de caractères
|
|
|
|
|
def max_list(L) :
|
|
|
|
|
k = len(L)
|
|
|
|
|
max, x = L[0], 0
|
|
|
|
|
i = 1
|
|
|
|
|
while i < k :
|
|
|
|
|
if max < L[i]:
|
|
|
|
|
max = L[i]
|
|
|
|
|
x = i
|
|
|
|
|
i = i + 1
|
|
|
|
|
return max, x
|
|
|
|
|
|
|
|
|
|
2. Fichier de programme
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
Pour utilser les fonctions définies dans d’autres fichiers, le fichier de
|
|
|
|
|
programme doit commencer par les instructions qui importent ces fichiers de
|
|
|
|
|
fonctions ou directement les fonctions de ces fichiers. Dans la syntaxe
|
|
|
|
|
ci-dessous, on importe une ou toutes les fonctions du fichier `foncmaxlist.py`.
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
# Fichier progmaxlist.py
|
|
|
|
|
from foncmaxliste import max_list
|
|
|
|
|
# ou plus simple:
|
|
|
|
|
# from foncmaxliste import *
|
|
|
|
|
print max_list([4,5,6,9,12,5,10,3,18,5,6,7])
|
|
|
|
|
couple = max_list([4,5,6,9,12,5,10,3,18,5,6,7])
|
|
|
|
|
print ’Max de L est ’, couple[0]
|
|
|
|
|
print ’et se trouve à la position ’, couple[1]
|
|
|
|
|
print max_list(’totovaaumarche’)
|
|
|
|
|
couple = max_list(’totovaaumarche’)
|
|
|
|
|
print ’Max de L est ’, couple[0]
|
|
|
|
|
print ’et se trouve à la position ’, couple[1]
|
|
|
|
|
|
|
|
|
|
Au lieu d’importer les fonctions, on peut importer le fichier qui définit les
|
|
|
|
|
fonctions avec la syntaxe qui suit. Dans ce cas, le fichier de programme sera
|
|
|
|
|
changé comme suit :
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
# Fichier prog2maxlist
|
|
|
|
|
import foncmaxliste
|
|
|
|
|
print foncmaxliste.max_list([4,5,6,9,12,5,10,3,18,5,6,7])
|
|
|
|
|
# la syntaxe indiquant le chemin d’acces a la fonction max_list utiliser ‘‘.’’
|
|
|
|
|
couple = foncmaxliste.max_list([4,5,6,9,12,5,10,3,18,5,6,7])
|
|
|
|
|
print ’Max de L est ’, couple[0]
|
|
|
|
|
print ’et se trouve à la position ’, couple[1]
|
|
|
|
|
print foncmaxliste.max_list(’totovaaumarche’)
|
|
|
|
|
couple = foncmaxliste.max_list(’totovaaumarche’)
|
|
|
|
|
print ’Max de L est ’, couple[0]
|
|
|
|
|
print ’et se trouve à la position ’, couple[1]
|
|
|
|
|
|
|
|
|
|
L’exécution directe du premier fichier de programme::
|
|
|
|
|
|
|
|
|
|
python prog max list.py
|
|
|
|
|
|
|
|
|
|
L’exécution directe du seconde fichier de programme::
|
|
|
|
|
|
|
|
|
|
python prog2 max list.py
|
|
|
|
|
|
|
|
|
|
Définition de l'implémentation d'un module
|
|
|
|
|
-------------------------------------------
|
|
|
|
|
|
|
|
|
|
Tout fichier qui contient au moins une définition d’une fonction ou d’une
|
|
|
|
|
variable est appelé un module (une bibliothèque). Le nom du module est le nom
|
|
|
|
|
du fichier enlevé le suffixe `.py`. Ainsi, un fichier de programme qui contient
|
|
|
|
|
au moins une définition d’une fonction ou un fichier qui ne contient que des
|
|
|
|
|
définition de fonctions sont des modules. On peut importer un module ou des
|
|
|
|
|
fonctions ou variables d’un module dans un programme, comme nous avons vu dans
|
|
|
|
|
les exemples ci-dessus.
|
|
|
|
|
|
|
|
|
|
.. important:: on peut importer un module, ou bien lancer un module en tant que
|
|
|
|
|
programme executable
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|
|
|
|
|
|
|
|
|
|
Pour faciliter la programmation, Python définit un certain nombre de **modules internes**,
|
|
|
|
|
appelés les builtins (la librairie standard).
|
|
|
|
|
|
|
|
|
|
Par exemple :
|
|
|
|
|
|
|
|
|
|
– Lors de l’ouverture d’une session interactive, on est dans un module interne nommé
|
|
|
|
|
main . Toutes les variables définies par affectation au niveau de ce module sont valides
|
|
|
|
|
globalement dans la session.
|
|
|
|
|
|
|
|
|
|
– D’autres modules internes sont string, math, random
|
|
|
|
|
|
|
|
|
|
Dans une session de travail sous l’interpréteur Python, la première importation d’un mo-
|
|
|
|
|
dule qui, à part des fonctions qu’elle définit, contient des instruction de programme fait
|
|
|
|
|
exécuter ces instructions. Dans la même session, les importations suivantes ne font pas
|
|
|
|
|
exécuter ces instructions. Pour les exécuter, on utilise la fonction reload(nomdumodule)
|
|
|
|
|
(sans sufffixe .py).
|
|
|
|
|
|
|
|
|
|
Exemples d'interface
|
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
type: son type
|
|
|
|
|
arguments
|
|
|
|
|
arg1 : description de l'argument 1
|
|
|
|
|
arg2 : description de l'argument 2
|
|
|
|
|
préconditions:
|
|
|
|
|
arg1 > 10
|
|
|
|
|
postconditions:
|
|
|
|
|
result < 19
|
|
|
|
|
raises: TypeError, AssertionError, SystemError...
|
|
|
|
|
test: tests nominaux pour chaque cas spécifié
|
|
|
|
|
|
|
|
|
|
- L'interface racine carrée
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
racine:
|
|
|
|
|
type: float -> float
|
|
|
|
|
arguments x: float, flottant dont on veut calculer la racine
|
|
|
|
|
pré: x >= 0
|
|
|
|
|
test: racine 25.0 -> 5.0 ; racine (-25) -> raises TypeError
|
|
|
|
|
|
|
|
|
|
- L'interface `lendemain`
|
|
|
|
|
|
|
|
|
|
Il faut définir auparavant un type spécifique appelé `date`
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
lendemain: le lendemain est la date qui désigne
|
|
|
|
|
le jour suivant de la date passée en argument
|
|
|
|
|
type: date -> date
|
|
|
|
|
arguments :
|
|
|
|
|
d: date
|
|
|
|
|
description: la date dont on veut calculer le lendemain
|
|
|
|
|
|
|
|
|
|
Le langages des modules
|
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
|
|
.. code-block:: ocaml
|
|
|
|
|
|
|
|
|
|
module type PILE = (* signature (interface) du module *)
|
|
|
|
|
sig
|
|
|
|
|
type ’a t
|
|
|
|
|
val create : unit -> ’a t
|
|
|
|
|
val push : ’a -> ’a t -> unit
|
|
|
|
|
val pop : ’a t -> ’a
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
(* implémentation du module *)
|
|
|
|
|
module Pile : PILE = (* le module est restreint
|
2017-10-12 18:21:33 +02:00
|
|
|
|
par la signature PILE *)
|
2017-08-28 17:36:36 +02:00
|
|
|
|
struct
|
|
|
|
|
type ’a t = ’a list ref
|
|
|
|
|
let create () = ref []
|
|
|
|
|
let push x p = p := x::!p
|
|
|
|
|
let pop p = match !p with [...]
|
|
|
|
|
let rec print p = match p with [...]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
- `struct .. end` introduit une collection de définitions, valeurs, types ou modules.
|
|
|
|
|
C'est une **structure**.
|
|
|
|
|
|
|
|
|
|
- `module Nom = struct .. end` permet de donner un nom à cette structure et
|
|
|
|
|
c'est ça un module. C'est une structure nommée.
|
|
|
|
|
|
|
|
|
|
- `sig ... end` introduit une signature de module : une interface pour un module.
|
|
|
|
|
|
|
|
|
|
On restreint souvent une structure par une signature pour "cacher" certaines
|
|
|
|
|
définitions. Une signature de module fournit une **interface** entre l'extérieur
|
|
|
|
|
et l'intérieur d'un module.
|
|
|
|
|
|
|
|
|
|
En dehors du module, on accède à ses composants grâce à la notation pointée
|
|
|
|
|
|
|
|
|
|
.. code-block:: ocaml
|
|
|
|
|
|
|
|
|
|
let p = Pile.create()
|
|
|
|
|
Pile.push 45 p
|
|
|
|
|
|
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
**Signature inférée** : Lorsque la déclaration d'interface n'existe pas.
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
Contrainte de type par signature
|
|
|
|
|
------------------------------------
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. code-block:: ocaml
|
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
module M =
|
|
|
|
|
struct
|
|
|
|
|
type t = int * int * int
|
|
|
|
|
let make d m y = d,m,y
|
|
|
|
|
end
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
module M :
|
|
|
|
|
sig type t = int * int * int
|
|
|
|
|
val make : ’a -> ’b -> ’c -> ’a * ’b * ’c end
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
.. ifconfig:: exercice
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
ici la signature inférée est du type le plus général.
|
|
|
|
|
Cela peut poser des difficultés. Lesquelles ?
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
.. ifconfig:: correction
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
une incohérence entre type attendu et type obtenu
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
.. code-block:: ocaml
|
|
|
|
|
|
|
|
|
|
# let d = M.make 52 24 137 ;;
|
|
|
|
|
val d : int * int * int = (52, 24, 137)
|
|
|
|
|
|
|
|
|
|
.. code-block:: ocaml
|
2017-08-28 17:36:36 +02:00
|
|
|
|
|
|
|
|
|
module M =
|
|
|
|
|
struct
|
|
|
|
|
type t = int * int * int ;;
|
|
|
|
|
let make d m y = d, m, y ;;
|
|
|
|
|
end ;;
|
|
|
|
|
|
|
|
|
|
let d = M.make 8 5 8 ;;
|
|
|
|
|
|
|
|
|
|
module type S =
|
|
|
|
|
sig
|
|
|
|
|
type t ;;
|
|
|
|
|
val make : int -> int -> int -> t ;;
|
|
|
|
|
end ;;
|
|
|
|
|
|
|
|
|
|
module MS = (M:S) ;;
|
|
|
|
|
|
|
|
|
|
MS.make 5 1 2 ;;
|
|
|
|
|
|
2017-10-12 18:21:33 +02:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2017-08-28 17:36:36 +02:00
|
|
|
|
Type et signature
|
|
|
|
|
------------------
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
# module type A = sig
|
|
|
|
|
val a: int -> int
|
|
|
|
|
end ;;
|
|
|
|
|
module type A = sig val a : int -> int end
|
|
|
|
|
# module B = struct
|
|
|
|
|
let a x = x + 1 ;;
|
|
|
|
|
end;;
|
|
|
|
|
module B : sig val a : int -> int end
|
|
|
|
|
# module C = (B:A) ;;
|
|
|
|
|
module C : A
|
|
|
|
|
|
|
|
|
|
# C.a 2 ;;
|
|
|
|
|
- : int = 3
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
Module auquel on impose une signature
|
|
|
|
|
-----------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
module type DATE = sig
|
|
|
|
|
type t
|
|
|
|
|
val make: int -> t
|
|
|
|
|
val get_year: t -> int
|
|
|
|
|
val get_month: t -> int
|
|
|
|
|
end ;;
|
|
|
|
|
|
|
|
|
|
module MR = struct
|
|
|
|
|
type t = int * int
|
|
|
|
|
let make x y = (x, y)
|
|
|
|
|
let get_month (x, y) = x
|
|
|
|
|
let get_year (x, y) = y
|
|
|
|
|
end ;;
|
|
|
|
|
|
|
|
|
|
module date = (MR:DATE) ;;
|
2017-10-12 18:21:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|