Active pattern broken in F# 3.0
Asked Answered
T

4

8

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
Trail answered 15/8, 2012 at 22:26 Comment(5)
Works fine for me, Visual Studio 2012 RC updated, Microsoft (R) F# 3.0 Interactive build 11.0.50522.1. Seeing the error, I still think it should work (as it does). The example in the error (let (|A|B|) (x:int) = A x) does indeed present the error you have posted.Kasper
F# 2.0 Interactive build 4.0.40219.1 gives exactly the same results.Kasper
Sorry, I should have been more specific about the environment. I've updated the question.Trail
I'll try to get my hands on the RTM version from my university, but until then I cannot help you :(Kasper
It's only a day old and still has the 'new software' smell, so no worries.Trail
D
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.

Defective answered 16/8, 2012 at 16:51 Comment(1)
Has this been fixed for F# 3.1?Leucopenia
M
3

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).

Menefee answered 16/8, 2012 at 15:12 Comment(2)
Thanks for the workaround. I can't imagine a "good reason" for breaking something that works in a previous version (and is useful). :-)Trail
The workaround in my answer (inspired by kvb's) to a follow-up question doesn't require the witness parameter and retains the original usage.Trail
S
2

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.

Sporocarp answered 16/8, 2012 at 23:25 Comment(0)
G
0

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.

Guenther answered 16/8, 2012 at 15:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.