How can I define and use an active pattern with a non-inferrable type argument?
I tried the followin开发者_运维技巧g:
let inline (|OpAdd|_|) (aty:Type, x:obj, y:obj) =
if aty.Equals(typeof<'a>) then Some(box ((unbox<'a> x) + (unbox<'a> y)))
else None
//FSI given signature:
//val inline ( |OpAdd|_| ) : Type * obj * obj -> obj option
Which gives no warnings or errors, but I can't figure out how to pass the explicit type argument at the call site and it seems that 'a
is always inferred as int
.
When I try to place the explicit parameter in the definition, I get a couple warnings and errors:
let inline (|OpAdd|_|)<'a> (aty:Type, x:obj, y:obj) =
if aty.Equals(typeof<'a>) then Some(box ((unbox<'a> x) + (unbox<'a> y)))
else None
warning FS1189: Type parameters must be placed directly adjacent to the type name, e.g. "type C<'T>", not type "C <'T>"
error FS0001: The declared type parameter 'a' cannot be used here since the type parameter cannot be resolved at compile time
Is it possible for active patterns to have explicit type parameters? If so how do I define and use them?
I'm not sure whether there is a clean way to do this (probably not, but I may be wrong).
As a dirty workaround, you can add a dummy parameter of type 'T
(when working with primitive types that have easy-to-create values) or of type Expr<'T>
(when you don't really want to create an instance). Then you can use the pattern with some dummy parameter that specifies the type:
let inline (|OpAdd|_|) (e:Expr<'T>) (aty:Type, x:obj, y:obj) =
if aty.Equals(typeof<'T>) then Some(box ((unbox<'T> x) + (unbox<'T> y)))
else None
let dummy<'T> : 'T = failwith "!"
match (typeof<float>, box 1.1, box 2.1) with
| OpAdd <@ dummy<int> @> res -> res
| OpAdd <@ dummy<float> @> res -> res
| _ -> null
I don't think there's any way to do what you want because I don't think it really makes sense. How would the compiler infer the type argument when the active pattern is used?
That is, given
function
| OpAdd x -> ...
how would the compiler know what 'a
should be?
As I see it, you have two options. One is to reflect 'a
in the return type of your pattern:
let inline (|OpAdd|_|) (aty:System.Type, x:obj, y:obj) =
if aty.Equals(typeof<'a>) then Some((unbox<'a> x + unbox<'a> y) : 'a)
else None
let (OpAdd(x:int)) = typeof<int>, box 1, box 2
let (OpAdd(y:float)) = typeof<float>, box 3.0, box 4.0
The other is to reflect 'a
in one of the inputs to your pattern (perhaps along the lines of Tomas's answer).
精彩评论