For a function argument that is a tuple, when the argument is referenced by the argument name, you may or may not get the same tuple (as denoted by obj.ReferenceEquals)
Repro steps
Given
let source = [(1,1); (2,2)]
source
|> List.iter (fun outer ->
source
|> List.exists (fun inner -> obj.ReferenceEquals (outer, inner)) // but Seq.exists fails!!
|> fun found -> if not found then failwith "boom!")
printfn "Hooray!"
The output is "Hooray!" but if the List.exists is change to Seq.exists then the exception condition is hit.
Expected behavior
The argument, when reference by name, should always be the original object.
Actual behavior
Sometimes, and I'm not sure the conditions under which it decides, the tuple is split and recreated in a new Tuple object. Even in optimized code. Neither of the exists function are marked as inline.
Known workarounds
Don't do reference equality checks on tuples!
Related information
Using Ilspy, it can be seen that for the List.exists case the following function object is created:
internal class clo@6-1 : FSharpFunc<Tuple<int, int>, bool>
{
public Tuple<int, int> outer;
[DebuggerNonUserCode, CompilerGenerated]
internal clo@6-1(Tuple<int, int> outer)
{
this.outer = outer;
}
public override bool Invoke(Tuple<int, int> inner)
{
return object.ReferenceEquals(this.outer, inner);
}
}
And for Seq.exists the following is created:
internal class main@9-1 : FSharpFunc<Tuple<int, int>, bool>
{
public Tuple<int, int> outer;
[DebuggerNonUserCode, CompilerGenerated]
internal main@9-1(Tuple<int, int> outer)
{
this.outer = outer;
}
public override bool Invoke(Tuple<int, int> tupledArg)
{
int item = tupledArg.Item1;
int item2 = tupledArg.Item2;
Tuple<int, int> objB = new Tuple<int, int>(item, item2);
return object.ReferenceEquals(this.outer, objB);
}
}
For a function argument that is a tuple, when the argument is referenced by the argument name, you may or may not get the same tuple (as denoted by obj.ReferenceEquals)
Repro steps
Given
The output is "Hooray!" but if the
List.existsis change toSeq.existsthen the exception condition is hit.Expected behavior
The argument, when reference by name, should always be the original object.
Actual behavior
Sometimes, and I'm not sure the conditions under which it decides, the tuple is split and recreated in a new Tuple object. Even in optimized code. Neither of the exists function are marked as inline.
Known workarounds
Don't do reference equality checks on tuples!
Related information
Using Ilspy, it can be seen that for the
List.existscase the following function object is created:And for Seq.exists the following is created: