Error: FSharp.Reflection: Microsoft.FSharp.Core.FSharpOption`1 is not an F# union type
Hello,
I encountered a surprising error while using Fable.Elmish.Browser custom Traces.console:
Unable to process the message: [*removed*] Error: Microsoft.FSharp.Core.FSharpOption`1 is not an F# union type
getUnionCases Reflection.js:310
getCaseName Program.fs:25
ElmishDebug_getMsgNameAndFields Program.fs:47
program_4 Program.fs:54
update program.fs:92
[...]
And indeed the type object has undefined cases.
I verified in an F# interactive console and it does work as expected out of Fable:
> FSharp.Reflection.FSharpType.GetUnionCases(typeof<option<string>>);;
val it: Reflection.UnionCaseInfo array =
[|FSharpOption`1.None
{DeclaringType = Microsoft.FSharp.Core.FSharpOption`1[System.String];
Name = "None";
Tag = 0;};
FSharpOption`1.Some
{DeclaringType = Microsoft.FSharp.Core.FSharpOption`1[System.String];
Name = "Some";
Tag = 1;}|]
I added a unit test in Fable on my fork (see diff here) and it fails with the same error as above.
Is it a known shortcoming of the reflection implementation in Fable that option types are not considered union cases?
Or is it a bug that needs fixing?
(I suspect it's a known shortcomings, because it seems too big to have been missed by inadvertence... but strangely I couldn't find an issue mentioning this?)
Ah, actually the issue might not be where I though it was...
I'm still not sure whether it's normal that an option is not seen as a DU in Fable compiled code, but the root cause of my initial error seems to be a discrepancy in the behaviour of Microsoft.FSharp.Reflection.FSharpType methods vs Fable.Core.Reflection methods, probably due to the erasure of options.
The following code:
type MyUnion = | A of int | B of string
let testValue = Some (MyUnion.A 42)
Browser.Dom.console.log ({| TestValue = testValue |})
Browser.Dom.console.log ({| TestValueType = testValue.GetType() |})
Browser.Dom.console.log ({| IsUnionFS = Microsoft.FSharp.Reflection.FSharpType.IsUnion(testValue.GetType())|})
Browser.Dom.console.log ({| IsUnionFable = Fable.Core.Reflection.isUnion testValue |})
Browser.Dom.console.log ({| CaseNames = Fable.Core.Reflection.getCaseName testValue |})
Browser.Dom.console.log ({| CaseFields = Fable.Core.Reflection.getCaseFields testValue |})
Displays the following in a console:
The union case seen by Fable.Core.Reflection is actually not the option, but the wrapped value! (which is itself a custom DU)
So mixing reflection calls from both namespaces at the same time seems very error prone...
EDIT: this has been fixed here https://github.com/elmish/browser/pull/77
I'm still curious about your expert opinion on this:
- Is all the above expected?
- Is there something we could improve? In documentation at least?
- What's your recommendation regarding reflection in Fable? Prefer using
Microsoft.FSharp.ReflectionorFable.Core.Reflection?
Related https://github.com/fable-compiler/Fable/issues/2110