roc icon indicating copy to clipboard operation
roc copied to clipboard

Order matters when sets comparing sets/dicts nested in other types (tags, records, etc)

Open isaacvando opened this issue 1 year ago • 3 comments

All of these expects should pass, but the last one fails:

# Passes
expect Set.fromList [1, 2] == Set.fromList [1, 2]
# Passes
expect Set.fromList [1, 2] == Set.fromList [2, 1]
# Passes
expect Ok (Set.fromList [1, 2]) == Ok (Set.fromList [1, 2])
# Fails
expect Ok (Set.fromList [1, 2]) == Ok (Set.fromList [2, 1])

isaacvando avatar Sep 22 '24 04:09 isaacvando

Context from Brendan:

Wild guess. We aren't correctly resolving abilities and using the opaque set equality operator when it is wrapped in a tag. Instead, it is doing a raw comparison of the internal impl as if the opaque type wasn't there.

Anton-4 avatar Sep 24 '24 16:09 Anton-4

This issue also affects records. I've opened a separate issue #7144, but feel free to close it if you think it's the same underlying cause.

ageron avatar Oct 07 '24 06:10 ageron

As @bhansconnect suggested, I can confirm that isEq does not seem to be called. Here's a little module defining a CustomSet opaque type:

# CustomSet.roc
module [create, isEq]

CustomSet := { items : List U64 } implements [Eq]

create : List U64 -> CustomSet
create = \items ->
    @CustomSet { items }

isEq : CustomSet, CustomSet -> Bool
isEq = \@CustomSet { items: items1 }, @CustomSet { items: items2 } ->
    List.sortAsc items1 == List.sortAsc items2

And here's a program that creates two CustomSet values and compares them with == and isEq: the results differ.

# test-custom-set.roc
app [main] {
    pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
}

import pf.Stdout
import CustomSet

main =
    set1 = CustomSet.create [1, 2, 3]
    set2 = CustomSet.create [3, 2, 1]
    result1 = set1 == set2
    result2 = set1 |> CustomSet.isEq set2
    Stdout.line! (result1 |> Inspect.toStr)
    Stdout.line! (result2 |> Inspect.toStr)

The output is:

Bool.false
Bool.true

ageron avatar Oct 11 '24 20:10 ageron