2017-06-10 1 views
2

J'ai un fichier ML qui contient un module imbriqué. Par exemple:Affinement de la signature d'un module imbriqué dans la signature du parent

let f n = n + 1 

module type M_type = sig 
    val g : int -> int 
    val h : int -> int 
end 

module M : M_type = struct 
    let g n = n + 2 
    let h n = n + 3 
end 

let j n = n |> M.h |> M.g |> f 

Lorsque vous rédigez un MLI pour cette ML, je souhaite ne pas exposer M.h, mais je ne veux exposer M.g.

tels que les suivants:

module type M_type = sig 
    val g : int -> int 
end 

module M : M_type 

val j : int -> int 

La combinaison ci-dessus de ML et MLI ne compile pas.

L'idée est que mon module imbriqué M contient certaines fonctions que je souhaite exposer à d'autres fonctions dans le module parent, mais pas à un utilisateur du module parent.

Existe-t-il un moyen légal d'y parvenir? S'il n'y en a pas, quelle est la meilleure alternative pour réaliser ce genre de rétrécissement de la signature?

Merci!

+0

Pourquoi voudriez-vous ajouter 'reason' comme balise car il parle clairement d'un problème OCaml? Êtes-vous en train d'essayer de l'annoncer? Eh bien, je suppose que la balise 'ocaml' sera maintenant poluted par vos modifications ... – Lhooq

Répondre

6

Si vous regardez attentivement l'erreur du compilateur:

Module type declarations do not match: 
     module type M_type = sig val g : int -> int val h : int -> int end 
     does not match 
     module type M_type = sig val g : int -> int end 

vous verrez que le compilateur ne se plaint pas du module M, mais sur le type de module M_type.

En effet, la définition des deux type de module ne correspond pas:

module type M_type = sig 
    val g : int -> int 
    val h : int -> int 
end 

est pas compatible avec

module type M_type = sig 
    val g : int -> int 
end 

Il y a plusieurs façons de résoudre ce problème. Une possibilité est de ne pas utiliser le type de module comme contrainte de signature lorsque unneccessary: ​​

(* a.ml *) 
module M = struct 
    let g n = n + 2 
    let h n = n + 3 
end 

De même, le fichier MLI peut être écrit comme

(*a.mli*)  
module M : sig val g: int -> int end 

Une autre possibilité est de ne définir le type de module contraint

(* a.ml *) 
module type M_type = sig val g: int -> int end 
module M: sig include M_type val h: int -> int end = 
(* Note that the signature constraint above is not necessary *) 
struct 
    let g n = n + 2 
    let h n = n + 3 
end 

avec le fichier associé MLI:

(*a.mli*) 
module type M_type = sig val h: int -> int end  
module M : sig val g: int -> int end 

Il est également possible de définir un type de module étendu EXT pour le fichier .ml:

module type M_type = sig val g: int -> int end 
module type EXT = sig include M_type val h: int -> int end 
module M: EXT = struct 
    let g n = n + 2 
    let h n = n + 3 
end 
+0

Merci. Cette solution est géniale. – alqz

+0

J'ai juste une petite question. Suivant le dernier exemple, je trouve que je peux toujours mettre le module M: ​​M_type dans la MLI, même si la vraie définition du module M a un type de module EXT, pas M_type. Qu'est-ce qui explique cela?Je peux voir que 'EXT' inclut' M_type', mais est ce que le compilateur reconnaît aussi? – alqz

+0

Exactement, les contraintes de type module, 'module M: ​​Signature = ...', restreignent la signature du module. Cela permet de contrôler l'API exposée d'un module en masquant les détails d'implémentation internes. Par exemple, n'importe quel module peut être limité à 'sig end':' module M: ​​sig end = struct type t = A end'. Plus utile, il est assez idiomatique dans OCaml d'implémenter la structure de données comme un module 'M' qui expose un type abstrait' t' et une opération sur ce type. Par exemple, un ensemble de modules sera 'module Set = sig type t val union; t -> t -> t ... end = struct ... end', en masquant l'implémentation du type t. – octachron