Active pattern broken in F# 3.0

谁都会走 提交于 2019-12-30 17:38:43

问题


This active pattern compiles with F# 2.0:

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

but, in F# 3.0, emits the error:

Active pattern '|Value|_|' has a result type containing type variables that are not determined by the input. The common cause is a [sic] when a result case is not mentioned, e.g. 'let (|A|B|) (x:int) = A x'. This can be fixed with a type constraint, e.g. 'let (|A|B|) (x:int) : Choice = A x'

I tried:

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

and:

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

How can it be fixed?

Environments: Visual Studio 2012 (RTM) and FSI v11.0.50727.1

EDIT: Here's a simpler repro:

let (|X|) x = unbox x

回答1:


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.




回答2:


I did not install the new version yet, but I agree this looks a bit fishy. I guess there may be a good reason for this restriction, but your example in the other question seems quite compeling.

As a workaround, I think that adding a witness parameter (that is not used, but hints what the type of the result is going to be) could work:

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

Of course, this makes the use a bit uglier, because you need to come up with some argument. In the above, I used witness of type unit -> 'T, hoping that the following might compile:

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

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

If that does not work, then you can probably try using witness parameter of type 'T (but then you have to provide an actual function, rather than just a generic function).




回答3:


for the sake of completeness, one more workaround:

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

however it still will suffer from the problem mentioned by @kvb in another thread. Personally I'll prefer @kvb's version with parameterized active pattern.




回答4:


See my answer to your other question for some thoughts on how to work around the issue and one reason that such active patterns might be undesirable. I'm not sure whether the breaking change was intended.



来源:https://stackoverflow.com/questions/11978234/active-pattern-broken-in-f-3-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!