使用F#discriminated union alternative作为参数类型

时间:2013-01-14 13:22:17

标签: types f#

我正在尝试使用区分联合的特定成员作为参数类型。例如:

type SomeUnion =
   | A of int * int
   | B of string

type SomeType(A(i, j)) =
    member this.I = i
    member this.J = j

let a = A(10, 20)
let instance = SomeType(a)

但这是非法的语法,并且抱怨SomeType的param列表中的“Unexpected symbol”('in type definition'。这是有效的语法:

let doSomethingWithA (A(i, j)) = i + j

但是类型签名是SomeUnion -> int而不是A -> int,它抱怨模式匹配不完整(鉴于签名可以理解)。

这可能吗?我相信F#union成员被编译为CLR类,所以理论上似乎可能,但实际上是这样(即没有使用像反射这样的东西)?否则我想你必须做一些更详细的手动OOP方式,并且不能保证完全匹配。

3 个答案:

答案 0 :(得分:6)

我同意你不能模式匹配构造函数参数是令人惊讶的。 与普通会员合作。

也许你可以在构造函数中进行显式匹配,如果值错误则会出现运行时错误:

type SomeType(a) =
    let i, j = match a with | A(k, l) -> k, l
    member this.I = i
    member this.J = j

但是否则,必须明白A 类型。因此,doSomethingWithA的类型并不像您预期​​的那样令人惊讶。你必须忍受不完整的模式匹配警告。

答案 1 :(得分:5)

正如wmeyer指出的那样,DU的“A”情况根本不是一种类型,而只是联合类型的一个组成部分。

如果你真的想要自己重用一个联合案例,我看不到任何替代它使它成为一个明确的独立类型。

选项一是使用类型别名:

type TypeA = int * int

type SomeUnion =
    | A of TypeA 
    | B of string

type SomeType(a:TypeA) =
    let (i,j) = a
    member this.I = i
    member this.J = j

let a = (10, 20)
let instance = SomeType(a)

为了清楚起见,我在构造函数中添加了TypeA注释。

第二种选择是将其包装在单个案例DU中。

type TypeA = TA of int * int

type SomeUnion =
    | A of TypeA 
    | B of string

type SomeType(a:TypeA) =
    let (TA(i,j)) = a  
    member this.I = i
    member this.J = j

let a = TA (10, 20)
let instance = SomeType(a)

(请注意,匹配let (TA(i,j)) = a必须有额外的parens,以免与函数混淆。)

答案 2 :(得分:4)

我的F#有点生疏,但也许你可以做这样的事情(用单声道f#2测试)?

type SomeUnion =
   | A of int * int
   | B of string

type SomeType(i:int, j:int) =
    member this.I = i
    member this.J = j

let createSomeType(SomeUnion.A(i,j)) = new SomeType(i,j)
let a = A(10, 20)
let instance = createSomeType(a)