Skip to content
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

proposal: cmp: Equal #70161

Closed
jimmyfrasche opened this issue Nov 1, 2024 · 7 comments
Closed

proposal: cmp: Equal #70161

jimmyfrasche opened this issue Nov 1, 2024 · 7 comments
Labels
Milestone

Comments

@jimmyfrasche
Copy link
Member

Proposal Details

I find myself writing this a lot lately. Sometimes multiple times in a module because I don't want to export it or bother with creating internal/ packages just for this.

package cmp

func Equal[T comparable](a, b T) bool {
  return a == b
}

This comes up whenever you have Op and OpFunc where the latter variant takes an equality function. The body of Op just calls OpFunc with some local copy of the proposed.

Even with the shortest possible syntax proposed in #21498 this would still be (a, b) => a == b vs cmp.Equal[T] and T would often be inferred as this is basically always used as the argument to a function.

@gopherbot gopherbot added this to the Proposal milestone Nov 1, 2024
@gabyhelp
Copy link

gabyhelp commented Nov 1, 2024

Related Issues and Documentation

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

@ianlancetaylor ianlancetaylor moved this to Incoming in Proposals Nov 1, 2024
@ianlancetaylor
Copy link
Member

A minor nit: in order to correspond to cmp.Compare, the implementation in the cmp package should be something like

func Cmp[T comparable](a, b T) bool {
    if isNaN(a) && isNaN(b) {
        return true
    }
    return a == b
}

@dsnet
Copy link
Member

dsnet commented Nov 3, 2024

Wouldn't that have unexpected behavior for composite types like arrays, structs, and interfaces?

Equal([2]float64{123, 456}, [2]float64{123, 456}) // true
Equal([2]float64{nan, 456}, [2]float64{123, 456}) // false
Equal([2]float64{nan, 456}, [2]float64{123, nan}) // true

As far as I can tell, our options are:

  1. restrict the constraint to ordered (which greatly limits it's utility),
  2. don't explicitly treat NaNs as special (which causes inconsistency with Compare),
  3. accept this weird edge case, or
  4. use reflection to introspect on composite types on a per-field or per-element basis (which imposes a dependency on reflect for a relatively simple package).

@ianlancetaylor
Copy link
Member

Good points. I don't know if there is a reasonable way to do this entirely correctly.

@jimmyfrasche
Copy link
Member Author

My understanding was that the special case for NaNs in Compare was to make it total so it could be used by sorting. Is there any such necessity for equality?

Regardless, it seems like either choice would require specific documentation to say that it surprisingly doesn't conform to Compare or that it surprisingly does.

Personally I think the best thing to do would be not conform so that it's nothing more than a convenient way to write T's == function. If special NaN handling is added I'd have to continue writing little ==s functions to avoid having to mention the NaN issues in documentation where the assumption is the normal rules of equality are in effect.

@ianlancetaylor
Copy link
Member

I think it's pretty confusing if cmp.Compare(a, b) == 0 but cmp.Equal(a, b) returns false. That just seems like a hazard waiting to trip people up.

@jimmyfrasche
Copy link
Member Author

I retract the proposal then. It's of no use to me if it's not regular ==.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants