SE-0494: Add `isIdentical(to:)` Methods for Quick Comparisons to Concrete Types

Hello, Swift community!

The review of SE-0494: Add isIdentical(to:) Methods for Quick Comparisons to Concrete Types begins now and runs through October 6, 2025.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager by email or DM. When contacting the review manager directly, please put "SE-0494" at the start of the subject line.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at:

swift-evolution/process.md at main · swiftlang/swift-evolution · GitHub

Thank you for contributing to Swift!

John McCall
Review Manager

13 Likes

Great work everybody for getting this to review! I like the targeted additions to relevant types. These will 100% add value and help folks improve performance. Definitely agree this doesn’t belong on Equatable, and I like the simplicity of not trying to add a unified protocol.

2 Likes

The proposal mentions === but only to say that that operator is orthogonal and intended for AnyObject. But there’s obvious arguments for using it instead of isIdentical(to:).

Let me come at this from the other side—for an AnyObject, is there any circumstance in which one would implement isIdentical(to:) other than by simply returning the result ===?

If there isn’t, then for an AnyObject, there’s no reason to distinguish between the two semantics. Then for a non-AnyObject, wouldn’t there also be no reason to distinguish between the two semantics? And therefore === alone would suffice for non-AnyObjects as well.

5 Likes

First of all good work to the authors of this proposal.

I find the second example in the motivation section (which uses SwiftUI) a little bit contrived. In either version of FavoriteContactList, we have this line private let contacts: [Contact]. And because Contact is a struct, we don't actually need favorites in the original code to be a computed property, neither for Storage.wrapped in the memorization-form code.

I think this example using SwiftUI should be updated to include some modification on contacts, otherwise it should be omitted entirely.

It looks good on paper but it's not a good idea, as the feature being discussed is advanced/low level optimisation feature, the spelling for which must be more "scary" than a mere ===. I found this reasoning convincing.

2 Likes

Looks good, but can we please drop the "product engineer" language from Swift proposals? These are positions/roles used by some software companies, but not a general term used by neither the broader public, the Swift community or any previous proposals. Swift is used by all kinds of people, in all kinds of positions.

19 Likes

I think this API needs a much clearer name, and something that distinguishes it further from ==. In English, isIdentical is more or less a synonym to isEqual. If I came across this in some SwiftUI code, I would have no idea what it meant. isIdentical doesn’t seem to mean identity (of the Identifiable protocol), nor pointer equality.

The proposed documentation doesn’t make it any clearer:

/// Returns a boolean value indicating whether this array is identical to `other`.

Ok, sounds like equality.

/// Two array values are identical if there is no way to distinguish between them.

Ok, same as equality.

/// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` /// - `a == b` does not imply `a.isIdentical(b)`

Hmm, what?

To me, a non-compiler engineer, it’s still not clear what “identical” actually means. The underlying storage is the same so it must be the same? Or does it mean we can sometimes provide an O(1) shortcut, even if it’s not comparing underlying storage?

The proposal says: but there does exist an important “fast path” that can return true in constant time. So if we want a fast path that can return true, isn’t this just an implementation detail of equality checking?

static func == (lhs: Self, rhs: Self) -> Bool {
  if lhs._isIdentical(to: rhs) {
    return true
  } else {
    // O(n) comparison
  }
}

Reading the Alternatives Considered, this actually seems like the most logical public API:

Exposing Identity

Our proposal introduces a new instance method on types that uses some underlying concept of “identity” to perform quick comparisons between two instances. A different approach would be to return the underlying identity to product engineers. If a product engineer wanted to test two instances for equality by identity they could perform that check themselves.

There’s a lot of interesting directions to go with that idea… but we don’t think this is right approach for now. Introducing some concept of an “escapable” identity to value types like Array would require a lot of design. It’s overthinking the problem and solving for something we don’t need right now.

I’d vote for making the current proposal use an underscored method in the short-term and work on the above alternative long-term.

2 Likes

This isn't the only place that makes reference to equal values being distinguishable, e.g. on SetAlgebra's insert(_:) method:

Return Value

(true, newMember) if newMember was not contained in the set. If an element equal to newMember was already contained in the set, the method returns (false, oldMember) , where oldMember is the element that was equal to newMember. In some cases, oldMember may be distinguishable from newMember by identity comparison or some other means.

1 Like

No, this is not the same as equality. A few examples:

  • -0.0 compares equal to 0.0, but we can distinguish between them via the .sign property.
  • "Ă©" compares equal to "e\u{301}", but we can distinguish between them via the .utf8 view.
  • two sets with identical elements compare equal, but we can distinguish between them because their iteration order will usually be different.
  • ...
11 Likes

Right, but my point is that exposing a plain public isIdentical API is necessarily confusing, based on all the above points

1 Like

It seems to follow that what we need is to expose identity, as in the alternatives considered section.

I'd appreciate it if you could make your argument without taking random shots at compiler engineers. Neither the proposal author nor @Karoy_Lorentey (who first introduced this method name to the standard library code base) is a compiler engineer.

The proposal is offering "identical" as a term of art for this concept. Personally, I'm torn about whether coining a term of art is a good idea here, and I do have some concerns about programmers looking for an equivalent to (say) equals finding this and thinking it's the right way to write ==.

As someone who is a compiler engineer, if I wanted to be precise, I would probably say that the condition being captured here is that the values have the same representation. hasSameRepresentation(as:) is a bit of a mouthful, but I don't think it's problematically long for an operation that deserves a little bit of extra attention. But I'm not sure if other programmers have the same associations with the word "representation" as I do.

6 Likes

I'm in favour of adding this feature. I do like using === here, but I don't think a named function is a dealbreaker. For comparison, a number of the mathematical operators on Swift's numeric types have named-function equivalents in the stdlib.

I can buy not having a dedicated protocol for this feature, but if we ever do add one, we should consider adding a default implementation of isIdentical(to:) that is generated at compile time for BitwiseCopyable types. (Not a "today" problem!)

One thing that stood out from the proposal in re Optional conformance:

Because this extension needs no private or internal symbols from Standard Library, we can omit this extension from our proposal. Product engineers that want this extension can choose to implement it for themselves.

I know there's no formal protocol defined in the proposal, but we would not give this sort of advice if there were one because of the dangers of competing retroactive conformances. Since the implementation of Optional.isIdentical(to:) is trivial, why not provide it and save developers the duplicated effort?

5 Likes

This might be true for the specific implementations pitched in this proposal, but I don’t think this definition generalizes well. The proposal specifically does not provide a definition for this general operation, and especially not one that relates to the definition of equality. Specifically, I believe the proposal intends to permit the following:

struct SubSet<Element: Equatable> {
  let parent: Set<Element>
  var index: Int
  var length: Int

  func isIdentical(to other: SubSet<Element>) -> Bool {
    parent == other.parent && (
      (index == other.index && length == other.length) ||
      (index + length == other.index && other.index + other.length == index)
  )

  func ==(other: SubSet<Element>) {
    if isIdentical(to: other) { return true}
    if length != other.length { return false }

    let ours: [Element]
    if length < 0 {
      ours = _sort(parent, index-length..<index)
    } else {
      ours = _sort(parent, index..<index+length)
    }

    let theirs: [Element]
    if other.length < 0 {
      ours = _sort(other.parent, other.index-other.length..<other.index)
    } else {
      ours = _sort(other.parent, other.index..<other.index+other.length)
    }

    return ours == theirs
}

let foo = Set()
let sub1 = SubSet(parent: foo, index: 0, length: 3)
let sub2 = SubSet(parent: foo, index: 3, length: -3)
assert(sub1.isIdentical(to: sub2))

This allows one to quickly check if two SubSets refer to the exact same subset of the same parent Set without performing a full equality check. This is useful for building fast paths as shown in SubSet.==, and also when building algorithms atop data structures like SubSet.

This is why I recommended renaming this method to isKnownIdentical(to:).

1 Like

I mean, that's not even approximately a quick comparison, so while I won't deny that that's permitted (in some imagined generalization of a proposal that specifically doesn't propose generalization), I certainly don't think it's in the spirit of the methods that are being added.

I don’t follow—surely a few arithmetic operations are faster than a sorting and equality check over the whole subrange. This is a real operation that code in the wild actually performs.

That said, I’ve also revised my post to make my point clearer: this proposal introduces isIdentical(to:) as a term of art, but explicitly does not define it in terms of a relationship to ==.

It does a full equality check on the parent sets.

SubSet.== does a full equality check on the elements contained within each SubSet. SubSet.isIdentical(to:) does some quick arithmetic to determine if two SubSets refer to the exact same indices of the same parent Set. In this case, subSet.isIdentical(to: other) implies subSet == other, and code which works with elements stored in SubSets can use this property to implement a fast path that avoids the enumerated equality comparison.

Edited for clarity:
The whole point of this tangent was to illustrate a plausible implementation of isIdentical(to:) for which it would not be accurate to say that the two SubSets have the same representation, because one could use positive indexing while the other uses negative indexing. I apologize that it took such a digression to get to this statement.

Alright, I regret starting this derail, because this is a proposal review, not a code review of your SubSet type. I agree that isKnownIdentical(to:) would be better than isIdentical(to:).

3 Likes

Sorry, I really wasn’t “taking a shot,” I was trying to say that since I don’t work in low level code I may be more confused by this given lack of expertise than others who do.

7 Likes