F#在不匹配的情况下提取可区分联合内的值

F# Extract value within Discriminated Union without matching

我有以下歧视联盟 (DU) 声明:

type Book =
    | Dictionary of string[]
    | Novel of int[]
    | Comics of bool[]

一个例子:

let x = Dictionary [|"a"; "b"|]

如何在不进行模式匹配且不关心数组的数据类型的情况下提取 中数组的长度(在本例中:stringintbool)。注意:我无法控制 DU 声明;结果,我无法在 Book 中编写新的成员方法,例如 getArrayLength()

当然,我们可以通过以下方式实现:

match x with
| Dictionary (x: _[]) -> x |> Array.length
| Novel (x: _[]) -> x |> Array.length
| Comics (x: _[]) -> x |> Array.length

但是输入很多 x |> Array.length 很不方便。这是一个简单的例子,但是我们可以想到一个通用的问题:

type Animal =
   | Dog of DogClass
   | Cat of CatClass
   | Cow of CowClass
   ...

...和DogClassCatClass等可能会分享一些东西。我们想要获得 那个共享的东西 。例如。那些 类 继承自 AnimalClass,其中有 countLegs() 方法。假设有很多动物,对所有动物进行模式匹配,而 -> 之后的代码块几乎相同。我喜欢原则 DRY(不要重复自己)。

有什么方便的方法可以解决这个问题吗?

==

2019 年 10 月 21 日编辑

我也在寻找像这样的语法:

let numEles =
   match x with
   | _ (arr: _[]) -> x |> Array.Length
   | _ -> failwith "No identifiers with fields as Array."

let numLegs =
   match anAnimall with
   | _ (animal: ?> Animal) -> animal.countLegs()
   | _ -> failwith "Can't count legs because of not being an animal."

我觉得还是遵循了匹配的精神,但是好像不支持这种做法

实际上,这里无法绕过模式匹配。在某种程度上,DU 就是为此而建造的。由于您无法控制类型,因此您可以随时添加类型扩展:

type Book with
    member this.Length =
        match this with
        | Dictionary d -> d.Length
        | Novel n -> n.Length
        | Comics c -> c.Length

let x = Dictionary [|"a"; "b"|]
printfn "%d" x.Length // Prints 2

尽管如果您愿意,定义一个带有 length 函数的 Book 模块也同样有效:

module Book =
    let length b =
        match b with
        | Dictionary d -> d.Length
        | Novel n -> n.Length
        | Comics c -> c.Length

let x = Dictionary [|"a"; "b"|]
printfn "%d" (x |> Book.length) // prints 2

但是您至少需要在 Book 类型上编写一次模式匹配表达式。每个案例都由具有相同 属性 的数据组成这一事实并不能真正帮助您仍然需要单独识别每个案例。