Search code examples
reflectionf#discriminated-union

Strange behaviour when reflecting over F# Discriminated Union


The following code compiles and runs correctly:

type FooUnion = MyCase of int * string

FSharp.Reflection.FSharpType.GetUnionCases(typeof<FooUnion>)
|> Array.tryFind(fun a -> a.Name = "MyCase")
|> Option.map(fun d -> FSharp.Reflection.FSharpValue.MakeUnion(d, [| 1; "test" |]))
|> Option.bind(function | :? FooUnion as s -> Some s | _ -> None)

However, if I remove the fully qualified FSharp.Reflection and move it to an open statement, the code no longer compiles: -

open FSharp.Reflection

type FooUnion = MyCase of int * string

FSharpType.GetUnionCases(typeof<FooUnion>)
|> Array.tryFind(fun a -> a.Name = "MyCase")
|> Option.map(fun d -> FSharpValue.MakeUnion(d, [| 1; "test" |]))
|> Option.bind(function | :? FooUnion as s -> Some s | _ -> None)

with errors on the call to MakeUnion: -

  1. No overloads match the method 'MakeUnion' [ within the VS IDE ]
  2. error FS0001: This expression was expected to have type int
    but here has type string [ within FSI if I execute the code manually ]

Solution

  • The FSharpValue type contains a single MakeUnion method:

    static member MakeUnion : unionCase:Reflection.UnionCaseInfo * args:obj [] * ?bindingFlags:System.Reflection.BindingFlags -> obj
    

    But the FSharp.Reflection namespace contains an extension methods with a slightly different signature.

    The FSharp compiler only implicitly box the content of the args array when there is no overload, so opening the namespace require to change the code to:

    FSharpValue.MakeUnion(d, [| box 1; box "test" |])
    

    even if you prefix with the full namespace.