Bonjour à tous, j’apprends OCaml et je galère un peu avec les modules et les interfaces, par exemple, voyez ce petit bout de code :
module type Ai =
sig
type a
type b
val f : b -> a
end
module A = functor (S:Set.S) ->
struct
type a = S.t (* le champ t d'un module Set est le type de l'ensemble, cf http://caml.inria.fr/pub/docs/manual-ocaml-312/libref/Set.html *)
type b = S.elt (* le champ elt est le type des éléments de l'ensemble *)
let f x = ((S.singleton x):a) ;;
end;;
module Int_set = Set.Make(
struct let compare = Pervasives.compare
type t = int
end);;
module A_marche_pas : Ai = A(Int_set);; (* le module typé par l'interface *)
module A_marche_bien = A(Int_set);; (* le module typé par le moteur d'inférance *)
let bien = A_marche_bien.f 1;;
let pas_bien = A_marche_pas.f 1;;
(*
Error: This expression has type int but an expression was expected of type
A_marche_pas.b
*)
La signature du module A_marche_bien inférée par le compilo :
sig
type a = Int_set.t
type b = Int_set.elt
val f : Int_set.elt -> a
end
Lors de l'appel à A_marche_bien.f 1, 1 est bien inféré de type Int_set.elt = a
de plus cette signature me semble compatible avec Ai avec Ai.b = int étant donné que l'on a Ai.b = A_marche_pas.b = Int_set.elt = (le type t passé à Set.Make) = int
mais ça ne marche pas.
Donc, je suppose qu'il y a quelque chose que j'ai pas compris, est-ce qu'il y a une restriction que je ne vois pas, une histoire de dépendance fonctionnelle ?
Est-ce qu'il y a un moyen élégant pour résoudre le problème ou le contourner ?
Merci beaucoup
# Un début de solution
Posté par Zylabon . Évalué à 2.
J'ai trouvé un truc pour que ça marche, mais je reste curieux savoir pourquoi ça ne marchait pas, en effet, en créant le module ainsi
Ça marche. Mais je ne comprend pas pourquoi le compilo ne peut deviner cette égalité avec l'inférence. La gestion des modules n'a pas l'air super jolie. Ces signes = qui ne désignent pas une égalité, pas de transparence référentielle…
Je serais ravis de connaître les raisons de ces limites :)
Please do not feed the trolls
[^] # Fin de solution.
Posté par benja . Évalué à 1. Dernière modification le 24 février 2013 à 14:06.
C'est plus sympa que d'espèrer que le compilateur va briser l'abstraction de type, ce qui rendrait le language un peu caduque. I.e. vouloir faire en sorte que Ai.b = int c'est mal comprendre l'intérêt de ce système de typage, mais bon cela viendra avec la pratique, courage ! ;-)
http://caml.inria.fr/pub/docs/manual-ocaml-4.00/manual018.html
[^] # Re: Fin de solution.
Posté par benja . Évalué à 2.
Ok /me doit apprendre à lire plus loin que le titre :)
Bon quand tu fais X : SIG, ben SIG c'est SIG. donc SIG.a != un type inféré, c'est SIG.a point. Ensuite (X:SIG with type t = Y.t) ben c'est une autre signature.
[^] # Re: Fin de solution.
Posté par chimrod (site web personnel) . Évalué à 2.
Pour appuyer ta réponse, j'ajouterai que l'inférence de type se fait sur les signatures, pas sur l'implémentation de celles-ci.
Donc quand tu as :
L'implémentation de a et b est masquée, et leur type n'est pas déterminé; il s'agit d'un nouveau type que tu viens de créer, et non pas une appellation différente d'un type existant.
C'est utile si tu veux masquer l'implémentation d'un type. Par exemple, ton set a pour type t et tu n'as pas à savoir comment il est implémenté dans ton module. Tu n'as pas besoin de savoir que t est implémenté comme suit :
et le compilateur tu génèrera une erreur si tu passes ton tupple là où il fallait passer le type de ton set.
Donc : utiliser les type de module c'est bien, ça permet de créer des foncteurs partageant les même types, et simplifier la documentation. Par contre, réduire une module à un type, c'est mal, ça casse l'inférence de type, il vaut mieux laisser le compilateur inférer pour toi les types de ton module.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.