GithubHelp home page GithubHelp logo

Comments (9)

dsnet avatar dsnet commented on August 19, 2024 5

Properly supporting cyclic data structures is very challenging. Even though I added that TODO, over time I've become convinced that cmp shouldn't support cyclic structures by default in order to avoid further complicating its logic.

I'm aware that reflect.DeepEqual supports cyclic structures, but their definition of equality is strange and probably not what the user expects. Consider this example. The graphs have clearly different layouts, yet they are all considered the same.

I should note that some cyclic structures are only cyclic because the structure is conceptually a tree, but every node has a "weak" reference to the root. In those situations, you can use an Ignore option to prevent cmp from traversing back to the root.

If someone has a good design for supporting graph equality (and fits in well with the ecosystem of cmp.Option), I'll consider it. For now, I have no intention of adding this functionality.

from go-cmp.

dsnet avatar dsnet commented on August 19, 2024 1

We could save only the curPath.String() for this purpose I guess?

That's still approximately the same amount of work (since you need to do string formatting). Furthermore, the string is not sufficient for avoid all cases of ambiguity.

Also we can make this optional with an Option?

I'm open to that possibility.

Another option is not to save the path, have similar "weird" behavior of DeepEqual, but at least not get into an infinite recursion as the current behavior is.

I've always been bothered by the DeepEqual behavior and believe it would be a mistake to replicate it.

from go-cmp.

posener avatar posener commented on August 19, 2024

Thanks for the detailed explanation! 👍

What about this sort of solution?
Add a memory variable to the state struct, that will be a set of a pair of addresses: those addresses are addresses of variables that their comparisons was already started.
We can add the addresses to this set in the compareAny switch, in reflect.Struct case.
Then, in the compareAny switch, reflect.Ptr case, we can add a test that if the pointers point to addresses that are already in the set, we skip the recursive call.

from go-cmp.

dsnet avatar dsnet commented on August 19, 2024

In my opinion, it is not sufficient to simply just check whether we have visited a pointer before. If you read the rules of reflect.DeepEqual, that is exactly what they do:

As DeepEqual traverses the data values it may find a cycle. The second and subsequent times that DeepEqual compares two pointer values that have been compared before, it treats the values as equal rather than examining the values to which they point.

This approach is why reflect.DeepEqual has strange results where graphs of clearly different structures are considered equal.

I think you would need to do something like this:

  • After descending into a pointer, record the current cmp.Path and the pointer address.
  • Thus, when you encounter a pointer, check whether any pointer was already visited:
    • If neither the left nor right object had ever visited any of the pointers, recursively descend into both of them.
    • If only one side had visited the pointer previously, but not the other. Then, report unequal.
    • (both sides have previously visited pointers)
      • Report equal if the Paths associated with those pointers are equal.
      • Otherwise, report unequal because the Paths differ.

However, I don't know how to implement that efficiently. There's also an open question of what it even means for two Paths to be equal.

from go-cmp.

posener avatar posener commented on August 19, 2024

@dsnet sounds good,
What do you mean by "efficiently" what's inefficient in this?

from go-cmp.

posener avatar posener commented on August 19, 2024

About the tag of this issue, I think this is a bug, not enhancement.

from go-cmp.

dsnet avatar dsnet commented on August 19, 2024

What do you mean by "efficiently" what's inefficient in this?

The current implementation of cmp.Equal only has a single field for curPath. The Path is treated a stack where PathSteps are pushed to and popped from it.

Adding the logic described above (assuming it is correct; which I'm not convinced of) requires that you copy the entire Path at ever pointer indirection (since the original state.curPath field will continue to be mutated). I suspect that is going to be expensive.

About the tag of this issue, I think this is a bug, not enhancement.

I guess that's a matter of perspective. I'm going to keep the label as is.

from go-cmp.

posener avatar posener commented on August 19, 2024

We could save only the curPath.String() for this purpose I guess? Also we can make this optional with an Option?

Another option is not to save the path, have similar "weird" behavior of DeepEqual, but at least not get into an infinite recursion as the current behavior is.

from go-cmp.

posener avatar posener commented on August 19, 2024

Yay! Cheers!

from go-cmp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.