8

此活动模式与 F# 2.0 一起编译:

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

但是,在 F# 3.0 中,会发出错误:

活动模式 '|Value|_|' 具有包含不由输入确定的类型变量的结果类型。当没有提到结果情况时,常见原因是 [sic],例如,'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
4

4 回答 4

4

There was a bug in the F# 2.0 compiler where the compiler did incorrect analysis and bad code generation for certain Active Patterns with free type variables in the result; a simple repro is

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

which generates a weird warning at compile-time and compiles into seemingly incorrect code. I am guessing that our attempt to fix this bug (and restrict some crazy cases) in F# 3.0 also broke some legal code as collateral damage of the fix.

I'll file another bug, but for F# 3.0, it sounds like you'll need to use one of the workarounds mentioned in other answers.

于 2012-08-16T16:51:29.263 回答
3

我还没有安装新版本,但我同意这看起来有点可疑。我想这个限制可能有一个很好的理由,但是你在另一个问题中的例子似乎很有说服力。

作为一种解决方法,我认为添加见证参数(未使用,但暗示结果的类型)可能有效:

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

当然,这使得使用有点难看,因为你需要提出一些论据。在上面,我使用了 type 的见证unit -> 'T,希望下面的代码可以编译:

let witness () : 'T = failwith "!"

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

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

于 2012-08-16T15:12:46.073 回答
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 的带有参数化活动模式的版本。

于 2012-08-16T23:25:51.893 回答
0

有关如何解决该问题的一些想法以及此类活动模式可能不受欢迎的一个原因,请参阅我对您的另一个问题的回答。我不确定这个重大变化是否是有意的。

于 2012-08-16T15:38:47.127 回答