Skip to content

Define identity/equality operators - #710

Merged
annevk merged 22 commits into
mainfrom
noamr/equality
Oct 5, 2026
Merged

annevk merged 22 commits into
mainfrom
noamr/equality

Conversation

@noamr

@noamr noamr commented May 8, 2026 •

Copy link
Copy Markdown
Contributor
  • Identical to (or is) is for primitives or by-reference comparisons
  • Equal is for shallow comparison of structs/lists/maps or custom data types, and is used by internal map/lists algorithms such as list/contains. It checks for cyclical references internally, using a private-type visited list.
  • Primitive data types specify their own "considered identical" overload.
  • Data types must specify "considered equal" in order to participate in list-contains or be map keys.

Closes #664


Preview | Diff

@annevk annevk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need three types? I don't understand why we can't define equals for lists. I also think that giving "equivalent" a normative meaning will be quite awkward as we often use it in notes and such to mean something else.

I also somewhat strongly prefer "is" over "identical". As it becomes more common having to scan over the long form will be annoying.

@noamr

noamr commented May 10, 2026

Copy link
Copy Markdown
Contributor Author

Do we really need three types? I don't understand why we can't define equals for lists. I also think that giving "equivalent" a normative meaning will be quite awkward as we often use it in notes and such to mean something else.

I was going by the discourse on the issue... I am not sure if deep-equals/equivalent is necessary or if it currently would be used by anyone. I am ok with dropping it for the time being.

Sure we can define equals for lists/maps etc, I was going by the discourse.

I also somewhat strongly prefer "is" over "identical". As it becomes more common having to scan over the long form will be annoying.

"Identical to" was already defined for strings and in some sentences it fits more naturally, as in "a and b are identical". Both forms are exported so perhaps it's ok to leave the choice to embedders?

@annevk

annevk commented May 11, 2026

Copy link
Copy Markdown
Member

Okay, thinking about this some more I think we want to borrow more of the original issue's ideas:

  • For identity comparison we can reuse the terminology is/identical to from strings.
  • Separate defining equality for something from the equals operator. So that you can say struct's equality is unordered deep equals, for instance.
  • Use deep equals and unordered deep equals instead of equivalent.
  • We should assert if we are comparing something with equals/deep equals and one of the underlying items has no equality.

I'm not sure how we should deal with cyclic references or whether lists/maps/structs should have a default equality that's not null. Perhaps we should start out with them not having a default and asserting until we get a bit more comfortable with it.

@noamr

noamr commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

Okay, thinking about this some more I think we want to borrow more of the original issue's ideas:

  • For identity comparison we can reuse the terminology is/identical to from strings.
  • Separate defining equality for something from the equals operator. So that you can say struct's equality is unordered deep equals, for instance.
  • Use deep equals and unordered deep equals instead of equivalent.
  • We should assert if we are comparing something with equals/deep equals and one of the underlying items has no equality.

This seems unnecessary. It should fall back to shallow-equals/identity. E.g a map of map of structs where one of the struct items is a platform object - it's ok if those are compared by reference and everything else is deep-equals.

I'm not sure how we should deal with cyclic references or whether lists/maps/structs should have a default equality that's not null. Perhaps we should start out with them not having a default and asserting until we get a bit more comfortable with it.

@annevk

annevk commented May 11, 2026

Copy link
Copy Markdown
Member

For platform objects it seems okay to default (or enforce?) all of them to identity for their equality, but that should probably be in Web IDL.

@noamr

noamr commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

For platform objects it seems okay to default (or enforce?) all of them to identity for their equality, but that should probably be in Web IDL.

Not sure I like this model. Maybe it is better to defer deep equality for now? Not sure if it has existing users?

@annevk

annevk commented May 11, 2026

Copy link
Copy Markdown
Member

I think Sanitizer essentially wants something like that? Not having equals for lists or maps at all probably won't work.

@noamr

noamr commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

I think Sanitizer essentially wants something like that? Not having equals for lists or maps at all probably won't work.

Sanitizer just needs to define equality for the SanitizerConfig dictionaries such as SanitizerElement, and then all the map/list algorithms (mainly contains) should refer to that. It doesn't need to deeply compare entire lists.

@annevk

annevk commented May 11, 2026

Copy link
Copy Markdown
Member

I guess we don't have to define (unordered) deep equals for v1, but I think the point around equals and equality still stands. And so we'd still have to define somewhere what equality means for platform objects since I don't think we want it to default to is.

@noamr

noamr commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

I guess we don't have to define (unordered) deep equals for v1, but I think the point around equals and equality still stands. And so we'd still have to define somewhere what equality means for platform objects since I don't think we want it to default to is.

In what scenario wouldn't we want to default to 'is'? Looks to me like unless there is a special equality operator like in URLs or structs it's probably the natural fallback?
Eg for js object references it's probably the most useful default.

@noamr noamr changed the title Define 3 types of equality (identical, equal, equivalent) Define identity/equality operators May 11, 2026
@noamr

noamr commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

I guess we don't have to define (unordered) deep equals for v1, but I think the point around equals and equality still stands. And so we'd still have to define somewhere what equality means for platform objects since I don't think we want it to default to is.

In what scenario wouldn't we want to default to 'is'? Looks to me like unless there is a special equality operator like in URLs or structs it's probably the natural fallback? Eg for js object references it's probably the most useful default.

nevermind, I've looked at this again and was convinced. See new revision.

@annevk annevk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still miss a clear distinction between how you define equality for a concept and then how you compare it. Currently we do things like <dfn for=url>equals</dfn> but maybe it should actually be something like

A URL's equality, given a URL a and a URL b, is:

or maybe the <dfn> pattern is okay, but I don't see you employing that here for list and set (although you do for unordered).

Also for sets the unordered definition is the same as the ordered one, which seems wrong.

@noamr

noamr commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

I still miss a clear distinction between how you define equality for a concept and then how you compare it. Currently we do things like <dfn for=url>equals</dfn> but maybe it should actually be something like

A URL's equality, given a URL a and a URL b, is:

Yes, we need to add that to the URL spec. Thinking to do there what we do here for strings, where there is still a <dfn for> for compat but it is also defined in terms of the "global" equality.

or maybe the <dfn> pattern is okay, but I don't see you employing that here for list and set (although you do for unordered).

I think you missed this line, which accounts for both list and set:

A list A is equal to a list B if A has the same size as B and A[i] is equal to B[i] for all indices i of A.

Also for sets the unordered definition is the same as the ordered one, which seems wrong.

Removed the extra one.

Comment thread infra.bs Outdated
@noamr

noamr commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

I think this is ready for another review?

@JakobJingleheimer

Copy link
Copy Markdown

Heads up: there is a similar/related proposal going in TC39 (comparisons).

noamr and others added 11 commits October 3, 2026 14:45
- `Identical` (or `is`) is for primitives or by-reference comparisons
- `Equal` is for shallow comparison of structs or custom data types
- `Equivalent` is for deep comparison of maps/list/etc or custom data types

Closes #664
Co-authored-by: Addison Phillips <addison@lab126.com>
identity/equality now have an exported algorithm as well as a type-specific
"overload".

When using the equality algorithms, a private "visited" list ("equality context") is forwarded,
to avoid cyclical references.
noamr and others added 10 commits October 3, 2026 14:45
Replace the identity check, equality check, and equality context
machinery with three concepts:

* "is" (or "identical to"): values of primitive data types are
  identical when they have the same contents; other values only when
  they are the same instance.
* "equality": every data type has one, which is identity unless
  defined otherwise.
* "equals": a is b, or a and b are of the same data type and its
  equality returns true for them.

Lists, maps, and tuples define their equality by comparing their
contents in order using equals. Other structs only equal themselves
unless their definition specifies otherwise, e.g., using the new
itemwise equality. This keeps the identity semantics specifications
implicitly rely on for structs in lists and sets today.

Drop the cyclic reference detection. It asserted on acyclic input
(e.g., when comparing « « » » with a list containing it) and was not
passed through contains or other equalities. Instead, an example
cautions against comparing values that contain themselves.

Furthermore:

* Make map contains and get use equals, to match the requirement that
  no two keys are equal. Phrase the ordered set requirement the same
  way.
* Rename set's "equal" to "unordered equal", keeping "equal" and the
  set-equal ID for compatibility, and add an example contrasting it
  with equals.
* Drop the null and boolean identity statements and a stale string
  TODO, and clarify the number and byte sequence ones.
* Rename the section to "Identity and equality", as its old ID would
  clash with the equality definition.
* Add notes on using structs as map keys or set items and on changing
  a tuple into a struct.
* Fix formatting and add Noam Rosenthal to the Acknowledgments.

@annevk annevk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I significantly reworked this as explained in the commit I added. I'm now happy with it. If this looks good to you as well Noam I can do some more work on ensuring we have PRs for the specifications that need changes as a result.

I'm not 100% sure about giving structs and tuples different equality by default, but it does make sense I think.

@noamr

noamr commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

I significantly reworked this as explained in the commit I added. I'm now happy with it. If this looks good to you as well Noam I can do some more work on ensuring we have PRs for the specifications that need changes as a result.

I'm not 100% sure about giving structs and tuples different equality by default, but it does make sense I think.

Yea looks great! Thanks

annevk added a commit to whatwg/html that referenced this pull request Oct 5, 2026
annevk added a commit to whatwg/webidl that referenced this pull request Oct 6, 2026
annevk added a commit to whatwg/fetch that referenced this pull request Oct 6, 2026
annevk added a commit to whatwg/storage that referenced this pull request Oct 6, 2026
annevk added a commit to whatwg/url that referenced this pull request Oct 6, 2026
annevk added a commit to whatwg/fs that referenced this pull request Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Equality definition rough plan?

4 participants