Forum Programmation.autre [OCaml] Problème de type avec les modules

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
0
23
fév.
2013

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  . É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

    module A_marche_pas:(Ai with type b = Int_set.elt) = A(Int_set);;
    (*et on se retrouve avec
    module A_marche_pas :
     sig type a
         type b = Int_set.elt
         val f : b -> a
     end
    
    et donc 
    A_marche_pas.f (1:int);; de type A_marche_pas.a = <abstr>, comme on veut !
    
    *)
    
    

    Ç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  . Évalué à 1. Dernière modification le 24 février 2013 à 14:06.

      module A2 : (Ai with type b  = Int_set.elt) = A(Int_set);;
      
      let pas_bien = A2.f 1;;
      val pas_bien : A2.a = <abstr>
      
      

      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  . É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  (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 :

          module type Ai =
            sig
              type a
              type b
              val f : b -> a
            end
          
          

          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 :

            type 'a set =
              | Empty
              | Node of 'a set * 'a * 'a set * int
          
          

          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.