Pattern combining type test and literal

The active template in this question cannot be compiled after upgrading to RTM version 2012. It provides a way to run a type test and match a literal inside a single template. For example:

let (|Value|_|) value = match box value with | :? 'T as x -> Some x | _ -> None let getValue (name: string) (r: IDataReader) = match r.[name] with | null | :? DBNull | Value "" -> Unchecked.defaultof<_> | v -> unbox v 

Can this be done without an active circuit? I understand that you can use the defender when ( :? string as s when s = "" ), but it cannot be combined with other templates.

+2
f # active-pattern guard-clause
source share
2 answers

kvb (which does not do the same, since it assumes that the type test has completed successfully) can be modified to create a similar pattern:

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

However, there is a subtle difference in performance. The original active template is converted to:

 public static FSharpOption<T> |Value|_|<a, T>(a value) { object obj = value; if (!LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<T>(obj)) { return null; } return FSharpOption<T>.Some((T)((object)obj)); } 

those. he performs type test and casting. This use ( match x with Value "" -> ... ) means:

 FSharpOption<string> fSharpOption = MyModule.|Value|_|<object, string>(obj); if (fSharpOption != null && string.Equals(fSharpOption.Value, "")) { ... } 

In particular, the typed value returned from the template is mapped using typical compiler transformations for templates ( string.Equals for strings).

The updated template is converted to:

 public static FSharpOption<Unit> |Value|_|<T, a>(T x, a value) { object obj = value; if (LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<T>(obj)) { T y = (T)((object)obj); T y3 = y; if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<T>(x, y3)) { T y2 = (T)((object)obj); return FSharpOption<Unit>.Some(null); } } return null; } 

which uses general equality and is less efficient than matching with a literal. Usage is a bit simpler since equality is baked in the template:

 FSharpOption<Unit> fSharpOption = MyModule.|Value|_|<string, object>("", obj); if (fSharpOption != null) { ... } 

Anyway, it works. But I like the original better.

+1
source share

You should be able to use a parameterized active template:

 let (|Value|_|) vx = if unbox x = v then Some() else None 

Use should look exactly like yours.

Edit

While I do not know if the modified change was intentional, I believe that active templates with typical return types other than input types should usually be avoided. In combination with the type of output, they can easily mask subtle errors. Consider the following example using your original template (|Value|_|) :

 match [1] with | Value [_] -> "Singleton" | _ -> "Huh?" 

This does not seem to be what you have ever tried to do - it means that Value should only be used with literals; parametrized active templates allow this scenario.

+1
source share

All Articles