6

我想写这样的东西:

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

编译器抱怨NumExpExpin之间的冲突getValue。即使以下失败:

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是我希望能够将 2 '插入' Expa Dot(而不是 2 个浮点数),因为它使生成表达式更容易,但它们不能是 any Exp,只是数字。

编辑:我真正想知道的是两个 DU 中的两个案例是否可以被视为同一个实体(有点像Exp“包括” NumExp)。我现在意识到Exp.Num并且NumExp.Num是完全独立的实体。Tomas 提供了一种区分以下两种情况的好方法。

4

4 回答 4

14

如果您有两个具有冲突案例名称的可区分联合,则可以使用可区分联合案例的完全限定名称:

 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) -> // ...
于 2010-07-07T13:32:45.470 回答
2

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

于 2010-07-08T23:33:37.417 回答
2

您可以使用接口作为替代。这会增加一些语法开销,但这是我发现的最好的方法。

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"
于 2015-02-16T17:07:57.487 回答
-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

于 2010-07-07T22:25:38.930 回答