6

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

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 的参数列表中出现“类型定义中的意外符号 '('”。这是有效的语法:

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

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

那么这可能吗?我相信 F# 联合成员被编译为 CLR 类,所以理论上似乎可行,但实际上是否可行(即不使用反射之类的东西)?否则我想你必须做更冗长的手动 OOP 方式,并且不能保证完全匹配。

4

3 回答 3

7

我同意您无法对构造函数参数进行模式匹配是令人惊讶的。它确实适用于普通会员。

也许您可以在构造函数中进行显式匹配,以在值错误时获取运行时错误:

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

但除此之外,人们必须明白那不是A一种类型。因此,类型不符合您的预期也就不足为奇了。而且您将不得不忍受不完整的模式匹配警告。doSomethingWithA

于 2013-01-14T15:49:53.373 回答
6

正如 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必须有额外的括号,以免与函数混淆。)

于 2013-01-14T21:24:06.730 回答
5

我的 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)
于 2013-01-14T13:53:49.250 回答