Question

Je veux écrire quelque chose comme ceci:

type NumExp = Num of float

type Exp =
    | Num of float
    | Dot of NumExp * NumExp
    | Op of string * Exp * Exp

 let getValue (Num(n) : NumExp) = n

Le compilateur se plaint d'un conflit entre NumExp et Exp dans getValue. Même l'échec suivant:

let getValue (nn : NumExp) = match nn with | Num(n) -> n

Est-il possible d'utiliser le même dans les deux cas, les syndicats discriminés qui fonctionne avec des fonctions? Les définitions DU eux-mêmes sont OK.

Je veux utiliser le même cas pour éviter d'ajouter un niveau d'indirection comme

type Exp =
    | NumExpExp of NumExp
    | Dot of NumExp * NumExp
    | Op of string * Exp * Exp

dans la définition de Exp. Je sens que je manque quelque chose ici très basique.

La raison pour laquelle je NumExp est que je veux être en mesure de « bouchon » 2 Exps dans un Dot (au lieu de 2 flotteurs) car il rend plus facile générer des expressions, mais ils ne peut pas être Exp, juste numérique.

EDIT : ce que je voulais vraiment savoir est de savoir si les deux cas, dans les deux pourraient être traités DDVP comme la même entité (un peu comme Exp « y compris » NumExp). Je me rends compte maintenant Exp.Num et NumExp.Num sont complètement entités distinctes. Tomas offre une belle façon de distinguer les deux cas ci-dessous.

Était-ce utile?

La solution

Si vous avez deux syndicats discriminés avec des noms contradictoires des cas, vous pouvez utiliser le nom complet de l'affaire du syndicat discriminé:

 let getValue (NumExp.Num(n)) = n  

Un exemple plus complet ressemblerait à ceci:

let rec eval = function
  | Exp.Num(f) -> f
  | Exp.Dot(NumExp.Num(f1), NumExp.Num(f2)) -> 
      // whatever 'dot' represents
  | Exp.Op(op, e1, e2) ->
      // operator

Il utilise toujours les noms qualifiés, ce qui est probablement une bonne idée si les noms sont assez simples et il y a des cas contradictoires (ce qui pourrait conduire à une confusion).

EDIT: En ce qui concerne le partage des cas - il n'y a pas moyen automatique de le faire, mais vous pourriez avoir un cas dans votre Exp qui inclut simplement les valeurs de NumExp. Par exemple comme ceci:

type NumExp =
  | Num of float 

type Exp = 
  // first occurrence of NumExp is just a name, but F# allows us to reuse 
  // the name of the type, so we do that (you could use other name)
  | NumExp of NumExp  
  // other cases

Lors de l'écriture fonction eval alors vous écrire (notez que nous n'avons plus la question avec des conflits de noms, donc on n'a pas besoin des noms qualifiés):

| NumExp(Num f) -> f
| Op(op, e1, e2) -> // ...

Autres conseils

Lorsque cela est possible (par exemple en utilisant des variantes polymorphes en OCaml), vous pouvez faire beaucoup avec elle, mais (malheureusement) F # ne possède pas cette fonctionnalité de langage il est actuellement incapable d'exprimer ce que vous voulez utiliser des types syndicaux. Cependant, vous pouvez envisager d'utiliser à la place POO ...

Vous pouvez utiliser en remplacement . Cela ajoute un peu de frais généraux syntaxique, mais est la meilleure façon que j'ai trouvé de le faire.

type IExp = interface end

type NumExp =
        | Num of float
        interface IExp
type Exp =
        | Dot of NumExp * NumExp
        | Op of string * IExp * IExp
        interface IExp

// This function accepts both NumExp and Exp
let f (x:IExp) = match x with
    | :? NumExp as e -> match e with
        | Num v -> "Num"
    | :? Exp as e -> match e with
        | Dot (e1,e2) -> "Dot"
        | Op (op,e1,e2) -> "Op"
    | _ -> invalidArg "x" "Unsupported expression type"

// This function accepts only NumExp
let g = function
    | Num v -> "Num"

Juste une observation: Pourquoi avez-vous besoin les syndicats construits de cette façon

?

J'aurais choisi une des deux options:

type NumExp = Num of float

type Exp =
    | Num of float
    | Dot of float * float
    | Op of string * Exp * Exp

qui est plus simple, ou

type NumExp = Num of float

type Exp =
    | NumExp
    | Dot of float * float
    | Op of string * Exp * Exp

Dans ce second cas, votre fonction

let getValue (Num(n) : NumExp) = n

fonctionne comme vous avez une définition de NumExp maintenant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top