为什么某些表达式中的泛型类型在 F# 模式匹配中被匹配为 obj?

Vag*_*lov 5 generics f# pattern-matching

我有以下代码:

type Message<'a> = | Message of 'a

let handleMessage message =
    match box message with
    | :? Message<_> -> printfn "Message"
    | _ -> printfn "Not message"

let handleMessageEx message =
    match box message with
    | :? Message<int> -> printfn "Message"
    | _ -> printfn "Not message"

handleMessage <| Message 1
handleMessage <| Message (1 :> obj)
handleMessageEx <| Message 1
Run Code Online (Sandbox Code Playgroud)

F# Interactive 中的输出如下:

Not message
Message 
Message
Run Code Online (Sandbox Code Playgroud)

为什么第一个语句会导致“Not message”?即,当匹配装箱值时,F# 无法检测到它是泛型类型 Message<_> ,除非我指定底层类型,否则它将其设置为 object(因此匹配失败(消息 1))。

kvb*_*kvb 4

每当您看到_类型参数时,请将其视为您不关心的新类型参数。如果我们相应地调整你的第一个定义:

let handleMessage message =
    match box message with
    | :? Message<'_a> -> printfn "Message"
    | _ -> printfn "Not message"
Run Code Online (Sandbox Code Playgroud)

然后编译器给了我们这个有用的线索:

警告 FS0064:此构造导致代码不如类型注释指示的通用。类型变量“_a”已被限制为“obj”类型。

问题是类型参数必须被赋予一些特定的值,但是编译器没有基础来选择一个值,所以它默认为obj,这不是你想要的。

没有开箱即用的好方法,但您可以创建一个活动模式来稍微简化体验:https ://stackoverflow.com/a/2140485/82959 。

  • @VagifAbilov - 我不同意你对“printfn”示例的框架。在“func”的每个调用站点,编译器在编译时知道“a”的具体实例化,而在这里您希望实例化在运行时有所不同。这是一个根本的区别,并且 CLR 不支持任何有效的技术来编码后者,因此即使编译器被修改为执行您期望的操作,编译后的代码也需要使用反射来实现它。 (2认同)