views:

138

answers:

2

Discriminated unions and other primitive types in F# uses structural equality by default, and provides a generated override for the .Equals method. The F# equality operator apparently differs from the C# one in that it uses the .Equals method even for reference types, but when F# discriminated unions are used from C#, the default operator== for object is used, which checks for reference equality rather than structural equality.

Why does not F# generate a custom operator== for discriminated union types so that == gives the expected behaviour when used in other .NET languages?

+1  A: 

Such behaviour is defined by the language you are using and not by the language of origin of the type you are using.

Jon Harrop
But surely, operator== must be seen as a .NET concept rather than a C# concept, and F# needs to play nice with the rest of .NET...
Freed
The `==` operator is a C# thing. They used different names (`=` vs `==`) precisely because they do different things, and it is inherited from OCaml which did that.
Jon Harrop
@Jon - while that's true, the F# team made sure that the common arithmetic operators work across languages (e.g. `(+)` gets translated to `op_Addition`, which is what C# recognizes). They could have generated an `op_Equality` method just as easily.
kvb
@kvb: Do you mean for F#'s own numerical types like vectors and matrices?
Jon Harrop
@Jon: No, I mean if you create a definition like `type T() = static member (+)(_:T,_:T) = T()`, then you should also be able to use the `+` operator on instances of `T` in C#. This works because the F# compiled form creates a static method called `op_Addition`, which is also how C# overloads the `+` operator.
kvb
@kvb: I see. Presumably that was regarded as ok because it wasn't replacing existing C# functionality.
Jon Harrop
A: 

I'm not on the F# team, so I can only speculate, but here are a few potential reasons:

  1. If you want to use structural equality from within C#, you can just use the Equals method. C# provides ways to test for two distinct kinds of equality - why should F# force them to behave in the same way when a might prefer to be able to use reference equality?
  2. If you want to force C# to use structural equality, it's easy to do it yourself:

    type T = A | B of int with
      static member op_Equality(t:T,t2:T) = t = t2
      // or even static member (=)(t:T, t2:T) = t = t2
    
  3. There's a development cost to any feature, so even if there were a clear benefit to automatically generating an op_Equality, it might have been dropped in favor of higher priority features.

kvb
1. Yes, but == is not always reference equality, object.ReferenceEquals is. C# does use value equality for == where it makes sense, such as for strings (which is a reference type but does do a value comparison when using ==).
Freed
Interestingly enough, according to Reflector, `Object.ReferenceEquals` simply calls `==`.
Joel Mueller
@Joel - that's fine as, since the arguments are statically typed as object, which means the operator== accepting objects will always be used, even if the runtime type is something completely different.
Freed
@Freed - I don't necessarily disagree; I think that there are pros and cons to either approach. Frankly, I don't really like how C#'s `==` operator behaves - it's too easy to mistakenly fall back on reference equality. I think that using `Equals` is almost always a better choice, and that will work fine for F# types.
kvb