-
Notifications
You must be signed in to change notification settings - Fork 789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Potential Set equality regression #14598
Comments
Oh and the workaround is to be explicit about the type in |
at NUnit.Framework.Assert.AreEqual(Object expected, Object actual) This does not end up calling the existing equality from F#, you can see that the stack trace is fully "at Nunit". The "object,object" overload is doing some magic. My PR introduced implementation of IStructuralEquatable (but it is not getting called), my guess is that nunit is trying to reveal that on the "object,object" code path and then does it's own thing. |
Some more details.
This also corresponds to explanation here: I will now look and nunit's source and see how it trets the obj,obj case. |
Ok found it. nunit has a chain of comparers for the non-generic AreEqual(obj,obj) case. With this PR, IStructuralEquatable was introduced and has preference over IEnumerable When F# set did not have IStructuralEquatable interface, IEnumerable -based comparison kicked in and for that two empty sequences are equal even if they are of different T. We could alter the IStructuralEquatable implementation in F# to also allow success in case of different types, i.e. allow comparing e.g. Set and Set even. The interface works with a passed comparer, and it would be up to the comparer to compare objects even of unrelated types. Checking the codebase though, this would not be very F# idiomatic. Only exception is array, where the following three indeed equal each other when compared using the IStructuralEquatable interface with System.Collections.Generic.EqualityComparer.Default provided.
Note that the same does not hold for Set,List,Map,.. or any other F#-generated implementation. |
@dsyme , @vzarytovskii : At least for Set<> I could bring in an implementation that would try to find equality (in the IStructuralEquatable codepath) even for collections of different types, like it works for Arrays. The most obvious examples are like the one above - should empty collections of different typar be structurally equal to each other or not? |
To have another use case besides empty collections (as in - should empty collections for different typarg be considered structurally equal or not), here is slightly less trivial scenario: Should collections holding one value: 2.0 , 2.0f and 2.0m respectively be considered structurally equal.
Right now for Array they are, for List (and for Set after my latest change) they are not because F# comparison code typically takes a shortcut after typecheck, and returns false if the types being compared are not the same. |
Intuitively, I wouldn't expect the code to compile, since Edit: On testing this, It would be nice to enable a warning when anything is inferred to be of type obj, or passed into a function as an obj. In the absence of this, we use a test that bans the string "Assert.AreEqual" in .fs files. |
😭 I've been trying to fix #13298 on-and-off for ages and ages, but I couldn't get the tests passing. |
Is this one still on the radar to get fixed in dotnet 8? |
There is no clear/correct path for "fixing" the behavior apart from the warning of Could you try that please? (another idea would be a project-specified way of banning certain dangerous APIs, e.g. via a text file. In this case the untyped Assert.AreEqual(obj,obj). Right now we do not have an vehicle for that though) |
For what it's worth, at work we usually write strongly typed wrappers around this stuff (basically extending what FsUnitTyped does). |
The FS3559 to warn about 'obj' has landed, but unfortunately it does not work for Set.empty. However, when using Set<>, the infered type is not obj, but rather an "a' which supports comparison" ;; which is a different thing and not covered by the warning. Sample code for testing: module Assert =
let AreEqual (expected : obj, actual : obj) = ()
let a = Set.empty
Assert.AreEqual(Set.empty, a)
Conceptually, I am on the side of @Smaug123 for not using the 'obj' APIs, that clearly mitigates the problem. I do not think that "a' with comparison" should deserve a special treatment in the compiler inference, also the product of all possible constraints might be too large. What do you all think? |
That being said, having the warning for List.empty in the same context is already a big win! |
Ah of course, I hadn't thought of that 🤦 yeah, maybe this is in the realm of analysers rather than the compiler. |
Tracking in a new 'for analyzers` bag, closing this here. |
Hello, I believe I may have a encountered a quite recent regression with Sets.
Repro steps
Create script:
Expected behaviour
Running this with the
7.0.102
SDK works out fine.No exception is thrown.
Actual behaviour
When I run this with my latest local compiler, I got:
Related information
@T-Gro maybe this is related to a9c194a but that is a wild guess at this point.
Provide any related information (optional):
The text was updated successfully, but these errors were encountered: