2017-07-26 3 views
4

Je développe un petit langage de programmation au moyen de transformations AST. C'est-à-dire, à partir de la VM et en ajoutant lentement des couches qui aident le programmeur.OCaml variantes polymorphes dans la correspondance de modèle

Comme chaque couche sait comment transformer ses nouveaux types, je l'ai fait quelque chose comme ceci:

module AST0 = struct 

    type t = [ 
    | `Halt 
    | `In 
    | `Out 
    | `Add of (int * int * int) 
    | `Sub of (int * int * int) 
    ] 

    let lower (ast : t list) = ast 
end 

module AST1 = struct 
    type t = [ 
    AST0.t 

    | `Inc of int 
    | `Dec of int 
    | `Cmp of (int * int * int) 
    ] 

    let lower (ast : t list) = 
    let lower_one = function 
     | `Inc a -> [`Add (a, a, `Imm 1)] 
     | `Dec a -> [`Sub (a, a, `Imm 1)] 
     | `Cmp (a, b) -> [`Sub (13, a, b)] 
     | (x : AST0.t) -> AST0.lower [x]  (* <--- problem line *) 
    in 
    List.concat @@ List.map lower_one ast 
end 

Malheureusement je reçois l'erreur:

File "stackoverflow.ml", line 28, characters 8-20: 
Error: This pattern matches values of type AST0.t 
     but a pattern was expected which matches values of type 
     [? `Cmp of 'a * 'b | `Dec of 'c | `Inc of 'd ] 
     The first variant type does not allow tag(s) `Cmp, `Dec, `Inc 

Je pensais que depuis le compilateur est intelligent assez pour remarquer que je n'ai pas manipulé XY et Z variantes dans un cas de match arbitraire qu'il pourrait dire que x en AST1.lower ne sera jamais réellement l'un des Cmp ou Inc ou Dec. Cela ne semble pas être le cas.

Ai-je mal compris le système de type OCaml? Est-ce que je manque quelque chose d'évident? Est-ce une approche stupide?

Répondre

6

Vous ne pouvez pas contraindre localement le type d'un motif de casse. La contrainte de type : AST0.t applique également le type des autres modèles à AST0.t. C'est pourquoi votre code ne tape pas; `Inc n'est pas inclus dans AST0.t. Cependant, il existe une fonctionnalité intéressante dans OCaml exactement pour ce que vous voulez faire. Utilisez l'alias de modèle #AST0.t au lieu de la contrainte de type. Voir https://caml.inria.fr/pub/docs/manual-ocaml/lablexamples.html#sec46 pour plus de détails:

(* I fixed several other trivial typing problems *) 
    let lower (ast : t list) = 
    let lower_one = function 
     | `Inc a -> [`Add (a, a, 1)] 
     | `Dec a -> [`Sub (a, a, 1)] 
     | `Cmp (a, b, c) -> [`Sub (13, a, b)] 
     | #AST0.t as x -> AST0.lower [x]  (* <--- problem line *) 
    in 
    List.concat @@ List.map lower_one ast 

#AST0.t as x est non seulement une abréviation de (`Halt | `In | `Out | `And _ | `Sub _ as x) mais modifie également le type de x dans le côté droit de ->[> AST1.t]-[> AST0.t]. Vous pouvez l'utiliser comme AST0.t là.

+0

Donc, cela semble avoir fixé une de mes couches, mais pas une autre. Je regarde maintenant ... – tekknolagi

+0

Comme je m'y attendais, l'autre couche était cassée à cause de quelque chose de stupide que j'ai fait. Merci de votre aide! – tekknolagi