F# 3.0 中的活动模式出现故障。

8

这个活动模式可与F#2.0编译:

let (|Value|_|) value = // 'a -> 'T option
  match box value with
  | :? 'T as x -> Some x
  | _ -> None

但是,在 F# 3.0 中,会出现以下错误:

活动模式 '|Value|_|' 具有包含未由输入确定的类型变量的结果类型。常见原因是结果情况没有被提及,例如 'let (|A|B|) (x:int) = A x'。这可以通过类型约束来修复,例如 'let (|A|B|) (x:int) : Choice = A x'

我尝试过:

let (|Value|_|) value : 'T option = ...

并且:

let (|Value|_|) (value: 'U) = ...

该如何解决?

环境:Visual Studio 2012(RTM)和FSI v11.0.50727.1

编辑:以下是更简单的重现方法:

let (|X|) x = unbox x

对我来说很好用,Visual Studio 2012 RC已更新,Microsoft (R) F# 3.0 Interactive build 11.0.50522.1。虽然看到了错误,但我仍然认为它应该可以工作(因为它确实可以)。错误中的示例(let (|A|B|) (x:int) = A x)确实出现了你发布的错误。 - Ramon Snir
1
F# 2.0互动构建4.0.40219.1会产生完全相同的结果。 - Ramon Snir
抱歉,我应该在环境方面更具体一些。我已经更新了问题。 - Daniel
我会尝试从我的大学获取RTM版本,但在那之前我无法帮助你 :( - Ramon Snir
它才出生一天,还有“新软件”的味道,所以不用担心。 - Daniel
4个回答

4

在 F# 2.0 编译器中存在一个错误,当 Active Patterns 中的结果含有自由类型变量时,编译器会进行错误的分析和生成错误代码;一个简单的重现方式是

let (|Check|) (a : int) = a, None
//let (|Check|) (a : int) = a, (None : int option)

let check a = 
    match a with
    | Check (10, None) -> System.Console.WriteLine "10"
    | Check (20, None) -> System.Console.WriteLine "20"

check 10
check 20

这会在编译时生成奇怪的警告,并编译成貌似不正确的代码。我猜想我们在 F# 3.0 中尝试修复此漏洞(并限制某些疯狂情况)时,也会因修复而破坏一些合法代码。

我将提交另一个错误报告,但对于 F# 3.0 版本,听起来您需要使用其他答案中提到的其中一种解决方法。


这个问题在 F# 3.1 中已经修复了吗? - Gustavo Guerra

3
我还没有安装新版本,但我同意这看起来有点可疑。我想可能有一个很好的理由限制这个,但你在另一个问题中的例子似乎非常有说服力。
作为一种解决方法,我认为添加一个证人参数(未使用,但提示结果类型)可能有效:
let (|Value|_|) (witness:unit -> 'T) value : 'T option =
  match box value with 
  | :? 'T as x -> Some x 
  | _ -> None 

当然,这使得使用变得有点丑陋,因为您需要想出一些参数。在上面的例子中,我使用了类型为unit -> 'T的witness,希望以下代码可以编译:
let witness () : 'T = failwith "!"

match box 1 with 
| Value witness 1 -> printfn "one"

如果那不起作用,那么你可以尝试使用类型为'T的见证参数(但你必须提供一个实际的函数,而不仅仅是一个通用函数)。


谢谢你的解决方法。我无法想象在之前的版本中,为什么会破坏一些有效的功能(并且这些功能很有用)。 :-) - Daniel
我的回答中的解决方法(受到kvb启发)对于后续问题不需要witness参数,并保留了原始用法。 - Daniel

2
为了完整起见,再提供一个解决办法:
type Box<'R> = Box of obj

let (|Value|_|) ((Box x) : Box<'R> ) : 'R option =
  match x with 
  | :? 'R as x -> Some x 
  | _ -> None 

let check t =
    match Box t with
    | Value 1 -> printfn "one"
    | Value 2 -> printfn "two"

check 1 // one
check 2 // two

然而,它仍将受到 @kvb 在 另一篇帖子 中提到的问题影响。就我个人而言,我更喜欢 @kvb 的版本,它使用了参数化活动模式。


0

请查看我对你的另一个问题答案,了解如何解决该问题以及这种主动模式可能不理想的原因。我不确定是否有意进行了破坏性更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接