George
George

Reputation: 2801

In F#, how to use active expressions to check type variables

The use of type typedef<typename> as a literal target within a match expression or when supported by an active expression as shown here shows syntax error on the typeof<> portion of the expression. Am I doing something wrong?

(Note that the match is against an instance of Type and not an actual value of that type.)

let (|IsType|_|) (et:Type) t = if et.Equals(t) then Some() else None

let someFunc someType =
    match someType with
    | IsType (typeof<string>) _ -> "Yes"
    | _                         -> "No"

Interestingly, within an if..then expression it does work.

let someFunc someType =
    if typeof<string> = someType then "Yes"
    else "No"
    )

This circumstance would appear to be an oversight in the language.

Upvotes: 1

Views: 860

Answers (2)

George
George

Reputation: 2801

The recommendation is to use the when class to perform a dynamic test as demonstrated here:

match typeValue with
    | t when t = typeof<string> -> ...
    | t when t = typeof<int> -> ...
    ...

Upvotes: 0

Tomas Petricek
Tomas Petricek

Reputation: 243051

This is a restriction of the F# language syntax - the things that can appear in a pattern (even when it is an active pattern) have to be syntactically patterns. So you can write IsSomething 123 because IsSomething and 123 are valid patterns, but you cannot write IsType typeof<Foo> because typeof<Foo> cannot be parsed as a pattern.

One workaround is to pass the argument as a quotation - I would probably not do this because it looks uglier and it is slower than doing a normal if, but it works:

open Microsoft.FSharp.Quotations

let (|IsType|_|) (et:Expr<'T>) (t:System.Type) = 
  if t.Equals(typeof<'T>) then Some() else None

let someFunc someType =
    match someType with
    | IsType <@ Unchecked.defaultof<string> @> -> "Yes"
    | _ -> "No"

It uses Unchecked.defaultof just to get a quotation of type string - but you could provide any other quotation of the type, so IsType <@ "nada" @> would work too.

EDIT: In the F# language specification, the allowed syntax is defined in section 7:

pat-param :=
    | const 
    | long-ident 
    | [ pat-param ; ... ; pat-param ]
    | ( pat-param, ..., pat-param )
    | long-ident pat-param 
    | pat-param : type
    | <@ expr @>
    | <@@ expr @@>
    | null 

Upvotes: 2

Related Questions