Question

There is some difficulty in getting Option type dynamically. Suppose I have a function:

let printType x =
   if (box x) = null then printfn "the type is 'null'"
   else printfn "the type is %A" (x.GetType())

And we have the output here:

printType 3          // the type is System.Int32
printType (Some(3))  // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Int32]
printType None       // the type is null
printType null       // the type is null

How to differentiate between None and null when getting an expression's type?

Was it helpful?

Solution 2

Edit: The code quotation is not needed and typeof can be used instead, see @Tomas Petricek's answer

You could use a code quotation to get the type from None

open Microsoft.FSharp.Reflection

let printType x = 
    let typeOfX = <@ x @>.Type

    if not <| FSharpType.IsUnion(typeOfX) && (box x) = null then 
        printfn "the type is 'null'"
    else 
        printfn "the type is %A" typeOfX

Your input:

printType 3          // the type is System.Int32
printType (Some(3))  // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Int32]
printType None       // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Object]
printType null       // the type is 'null'

OTHER TIPS

At runtime, option None values are represented as null and so you cannot determine their runtime type. However, you could write a generic function that prints the static type of the option:

let printType (x:'T option) =
  printfn "Static type is: %s" (typeof<'T>.Name)

Depending on what you need, this might or might not do what you need.

printType (None : int option)     // Int32
printType (None : string option)  // String 
printType None                    // Object (the default used by the inference)

EDIT If you want to be able to call the function on any arguments, you can use typeof<'T> as in my example together with the rest of the logic as in the solution by Matthew. The following does the same thing as Matthew's snippet (but it does not create quotations for no good reason):

let printType (x:'T) =
  let typeOfX = typeof<'T>
  if not <| FSharpType.IsUnion(typeOfX) && (box x) = null then 
    printfn "Static type is %s, but the value is 'null'" typeOfX.Name
  else 
    printfn "Static type is %s and value is not 'null'" typeOfX.Name

This can be a common problem when implementing interpreters. To add to the above solutions, you could also do the following:

open System

type TagObj (obj : obj, t : Type) =
    member __.Obj = obj
    member __.Type = t

and TagObj<'T>(value : 'T) =
    inherit TagObj(value, typeof<'T>)

    member __.Value = value
    override __.ToString() = sprintf "%O" value

let mkTag x = TagObj<_>(x) :> TagObj

// examples

let t = mkTag (None : int option)
let t' = mkTag ()

t.Obj = t'.Obj // true
t.Type = t'.Type // false

This is roughly how constant values are represented in quotations as well.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top