It seems to me that some properties of the F# option type are not visible from C# projects. By inspecting the types, I can see more or less the reason, but I don't really understand what exactly is going on, why these choices have been made or how best to circumvent the issue.
Here's some snippets demonstrating the issue. I have a VS2015 solution containing two projects, a C# project and a F# project. In the F# project, I have a class defined as follows:
type Foo () =
member this.Bar () = Some(1)
Furthermore, in F# I can write something like this:
let option = (new Foo()).Bar()
let result = if option.IsNone then "Is none" else "Is some"
So it would appear that the option type has a property named IsNone
. Now, in the C# project, I have a reference to the .dll compiled from the F# project. This allows me to write e.g.
var optionType = new Foo().Bar();
The variable optionType
is an FSharpOption<int>
. As I noted above, when I use option types in F# projects, I usually have access to for example the IsSome
and IsNone
properties. However, when I try to write something like optionType.IsNone
, I get the CS1546 error "Property, indexer or event ... is not supported by the language". In concordance with this, Intellisense does not detect the property:
Now, when inspecting the FSharpOption type, I can see that the IsNone and IsSome "properties" appear as static methods:
On the other hand, when I inspect the type from F#, I see the following instead:
Here, the "existence" of the properties IsSome
and IsNone
is evident. Hovering the cursor over these properties, VS2015 gives me the following note: "The containing type can use 'null' as a representation value for its nullary union case. This member will be compiled as a static member." This is the reason why the properties aren't available except as static methods (as noted by lukegv and Fyodor Soikin).
So, the situation appears to be the following: The compiled FSharpOption type does not have any IsNone and IsSome properties. Something is going on behind the scenes in F# to enable functionality emulating these properties.
I know that I can get around this by using the OptionModule
in Microsoft.FSharp.Core. However, it seems that this functionality is a conscious choice by the architects of the F# core library. What are the reasons for the choice? And is using OptionModule
the right solution, or is there a better way to use the FSharpOption<T>
type from C#?
This has to do with how option
is compiled. The Some
values get compiled straightforwardly as creating an instance of the class and wrapping the value in it. But the None
values are not really values, they're just null
.
Try this:
let a: int option = Some 1
let b: int option = None
let a_isNull = obj.ReferenceEquals( a, null ) // a_isNull = false
let b_isNull = obj.ReferenceEquals( b, null ) // b_isNull = true
(this is also the reason that the None
values will show up as null
in the debugger's Watch window)
This is an optimization that saves a lot of cycles at runtime. (and you can use it for your own union types, too, by applying CompilationRepresentationFlags.UseNullAsTrueValue
)
Now, since some values of this type may be null
, you can't really use properties or methods on these values. If the value happens to be null
, you'll just crash. That's why you should always use the OptionModule
for all operations.
As for why these properties don't show up in intellisense - that's because they're static
. Though I'm not sure why they're present there at all. Perhaps a compiler artifact.
I am not well versed with F#, but since it is all CLR, my answer is from a C# point of view:
In the generated class definition, both IsNone
and IsSome
are static and therefor can't be accessed via the instance optionType
(neither via IntelliSense, nor in code). The property Value
is not static and can be accessed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With