输入DU的测试模式匹配

时间:2015-07-25 20:51:08

标签: f#

使用DU(Discriminated Union类型),如何执行类型测试模式匹配? 我有以下运行代码:

type IU =
|Int of int
|Unit of Unit

let x = IU.Int(3)
let y = IU.Unit(())
let z = [3.14]

let showI (v) = 
    match box v with
    | :? IU -> 
        match v with
        | Int(_) -> "an IU int"
        |_ -> "not a IU.int"
    |_ -> "not a IU.int"

但是我对showI函数中的内部匹配不满意。我更喜欢这样的东西:

let showI (v) = 
    match box v with
    | :? IU.Int -> "an int"
    |_ -> "not a IU.int"

没有编译(错误:未定义Int类型)。

我错过了明显的语法吗?感谢。

注意:showI函数接受具有未知类型的变量;这就是臭盒v。

的原因

2 个答案:

答案 0 :(得分:5)

正如其他人所指出的,我不认为有任何内置的语言功能可以让你这样做。但是,您可以定义执行类型测试的active pattern

let (|IsIU|_|) (candidate : obj) =
    match candidate with
    | :? IU as iu -> Some iu
    | _ -> None

此活动模式的类型为obj -> IU option

您可以使用标准模式构建自己的自定义活动模式,如下所示:

let showI = function
    | IsIU (IU.Int i) -> "an IU int"
    | _ -> "not a IU.int"

在此示例中,自定义IsIU活动模式由标准标识符模式组成,该模式与IU.Int大小写匹配。

这是一个示例FSI会话,显示OP中给出的xyz值的使用情况:

> showI x;;
val it : string = "an IU int"
> showI y;;
val it : string = "not a IU.int"
> showI z;;
val it : string = "not a IU.int"

答案 1 :(得分:1)

保持在您的问题的上下文中我相信您缺少的是IU.Int不是类型,而是案例 Int 歧视联合类型 IU。当你写

let x = IU.Int(3)

x的类型为IU,而不是IU.Int。这就是为什么编译器会因您尝试将objUI.Int:?模式匹配而咆哮。

在更广泛的背景下,您似乎尝试接近Javascript类型的F#a-la动态语言,但事实并非如此。夸大一点,你似乎尝试使用仅对一种类型obj的参数进行操作的函数,因此花费大量的运行时间来动态发现特定的参数类型,并在途中犯错误的广泛机会。

这种方法错过了F#惯用DU用例的全部内容,即通过模式匹配机制将已知静态类型为IU的值分解为特定的工会案例IU.IntIU.Unit):

let showI (v : IU) =  // explicit argument type is added to illuminate the point
    match v with
    | IU.Int(x) -> sprintf "a IU.Int(%i) value" x
    | _ -> "a IU.Unit"

因此,如果您错误地尝试使用不属于showI类型的参数调用IU,编译器将立即捕获错误类型参数的错误使用函数,并且不会构建代码的可执行形式,直到错误得到纠正。

编辑:除了惯用法之外,你可以在match后卫的帮助下躲过一个when,就像在下面的片段中一样,尽管这是讨厌的黑客:

open Microsoft.FSharp.Reflection

let showI (v) = 
    match box v with
    | :? IU as x when (fst(FSharpValue.GetUnionFields(x, typeof<IU>))).Name.Equals("Int")
        -> "an IU.Int"  
    | _ -> "not an IU.Int"