F#歧视联盟中的共享案例

时间:2010-07-07 12:52:40

标签: f# types discriminated-union

我想写这样的东西:

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

编译器抱怨NumExpExpgetValue之间存在冲突。 甚至以下都失败了:

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

有没有办法在两个使用函数的区分联合中使用相同的案例? DU定义本身就可以。

我想使用相同的情况来避免添加像

这样的间接级别
type Exp =
    | NumExpExp of NumExp
    | Dot of NumExp * NumExp
    | Op of string * Exp * Exp
Exp定义中的

。 我觉得我在这里遗漏了一些非常基本的东西。

NumExp的原因是我希望能够将Exp'插入'Dot(而不是2个浮点数),因为它可以更容易地生成表达式,但它们不能是任何Exp,只是数字。

编辑:我真正想知道的是两个DU中的两个案例是否可以被视为同一个实体(类似于Exp“,包括”{{1} })。我意识到现在NumExpExp.Num是完全独立的实体。 Tomas提供了一种区分下面两种情况的好方法。

4 个答案:

答案 0 :(得分:13)

如果您有两个具有相互冲突的案件名称的歧视联盟,您可以使用受歧视的工会案件的完全限定名称:

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

更完整的示例如下所示:

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

这总是使用完全限定的名称,如果名称足够简单并且存在冲突的情况(可能导致混淆),这可能是一个好主意。

编辑:关于案例分享 - 没有自动执行此操作的方法,但您的Exp中可能只包含NumExp的值。例如:

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

编写eval函数时,您将编写(请注意,我们不再存在名称冲突问题,因此我们不需要完全限定名称):

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

答案 1 :(得分:2)

如果可能的话(例如在OCaml中使用多态变体),你可以用它做很多事情但是(遗憾的是)F#没有这种语言特性,所以它目前无法用联合类型表达你想要的东西。但是,您可以考虑使用OOP ......

答案 2 :(得分:2)

您可以使用interfaces as a substitute。 这增加了一些语法开销,但这是我发现这样做的最好方法。

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"

答案 3 :(得分:-1)

只是一个观察:为什么你需要以这种方式构建的工会?

我会选择以下两个选项之一:

type NumExp = Num of float

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

更简单,或者

type NumExp = Num of float

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

在第二种情况下,您的功能

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

现在只有NumExp的定义。