In dotnet, arrays are compared as reference types, so 2 arrays with same items are not equal. In F# types are generally compared structurally, such that lists with same items are equal. While the list type origins from the f# library, the array type is the common array type from the base class library. Therefore
[| 1 |].Equals [| 1 |] // false
is false because that is how the dotnet and the BCL compare arrays. On the other hand the equality operator on arrays
[| 1 |] = [| 1 |] // true
gives true and compares arrays structurally. I would like to know where in the source code of the f# core library the function is that gets called when the (=) operator is applied to arrays.
I believe this is built into the compiler itself, but maybe not?
The Array module code is here, it does not answer this question however:
https://github.com/dotnet/fsharp/blob/fc42ec9bc18740ccc3a1e41ed1082cb091f5c31f/src/FSharp.Core/array.fsi
In F#, equality between two dotnet arrays via the equality operator is indeed performed structurally, unlike the Equals
method, which looks for references in the case of arrays.
This behavior is implemented in the compiler and not in the module dedicated to arrays, in fact you can see it in the generated code. Let's take this example, where I use comparison via the .Equals
method and via the equality operator for arrays and lists :
let a (x: _ array) (y: _ array) = x.Equals y
let b (x: _ array) (y: _ array) = x = y
let a' (x: _ list) (y: _ list) = x.Equals y
let b' (x: _ list) (y: _ list) = x = y
The generated (C#) code we are interested in is as follows:
[CompilationArgumentCounts(new int[] { 1, 1 })]
public static bool a<a, b>(a[] x, b[] y)
{
return x.Equals(y);
}
[CompilationArgumentCounts(new int[] { 1, 1 })]
public static bool b<a>(a[] x, a[] y)
{
return LanguagePrimitives.HashCompare.GenericEqualityIntrinsic(x, y);
}
[CompilationArgumentCounts(new int[] { 1, 1 })]
public static bool a'<a, b>(FSharpList<a> x, FSharpList<b> y)
{
return x.Equals(y);
}
[CompilationArgumentCounts(new int[] { 1, 1 })]
public static bool b'<a>(FSharpList<a> x, FSharpList<a> y)
{
return x.Equals(y, LanguagePrimitives.GenericEqualityComparer);
}
In particular, for arrays, you can see that the code generated for the equalities is as follows:
method x.Equals y |
operator x = y |
---|---|
x.Equals(y) |
LanguagePrimitives.HashCompare.GenericEqualityIntrinsic(x, y) |
and for lists, as comparaison:
method x.Equals y |
operator x = y |
---|---|
x.Equals(y) |
x.Equals(y, LanguagePrimitives.GenericEqualityComparer) |
The method LanguagePrimitives.HashCompare.GenericEqualityIntrinsic
is described as part of the optimizations proposed by the compiler, but is in fact called in the general generated code.
You can find the source of the implementation here in the current F# compiler code.
You can take a look at the code generated by SharpLab here.