Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are some properties (e.g. IsSome and IsNone) for FSharpOption not visible from C#?

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:

Intellisense does not detect the IsSome and IsNone properties

Now, when inspecting the FSharpOption type, I can see that the IsNone and IsSome "properties" appear as static methods:

FSharpOption class signature from C#

On the other hand, when I inspect the type from F#, I see the following instead:

FSharpOption class signature from F#

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#?

like image 936
Alexander Sokol Avatar asked Feb 01 '17 15:02

Alexander Sokol


2 Answers

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.

like image 94
Fyodor Soikin Avatar answered Sep 23 '22 22:09

Fyodor Soikin


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.

like image 29
Lukas Körfer Avatar answered Sep 20 '22 22:09

Lukas Körfer