问题
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