Skip to content

Commit 4c4c0ba

Browse files
authored
[patterns] Revise record identical() to be faster. (#2411)
* [patterns] Revise record identical() to be faster. Fix #2390. * Revise non-normative text.
1 parent b8eb49f commit 4c4c0ba

File tree

1 file changed

+27
-26
lines changed

1 file changed

+27
-26
lines changed

working/0546-patterns/records-feature-specification.md

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -322,38 +322,39 @@ through calls to `identical()`, then optimizing away the creation of these
322322
objects is harder.
323323

324324
Semantically, we do not want records to have unique identities distinct from
325-
their contents. A record *is* its contents in the same way that every value 3
326-
in a program is the "same" 3 whether it came from the number literal `3` or the
327-
result of `1 + 2`.
325+
their contents. A record *is* its contents in the same way that every value 3 in
326+
a program is the "same" 3 whether it came from the number literal `3` or the
327+
result of `1 + 2`. This is why `==` for records is defined in terms of their
328+
shape and fields. Two records with the same shape and equal fields are equal
329+
values.
328330

329-
This is why `==` for records is defined in terms of their shape and fields. Two
330-
records with the same shape and fields are equivalent. Identity follows similar
331-
rules. Calling `identical()` with a record argument returns:
331+
At the same time, we want `identical()` to be fast because one of its primary
332+
uses is as a fast-path check for equality. An `identical()` that is obliged to
333+
iterate over the record's fields (transitively in the case where some fields
334+
are themselves records) might nullify the benefits of using `identical()` as a
335+
fast-path check before calling `==`.
336+
337+
To balance those opposing goals, `identical()` on records is defined to only
338+
offer loose guarantees. Calling `identical()` with a record argument returns:
332339

333340
* `false`, if the other argument is not a record.
334341
* `false`, if the records do not have the same shape. *Since named field
335342
order is not part of a record's shape, this implies that named field order
336-
does not affect identity either. `(a: 1, b: 2)` and `(b: 2, a: 1)` are
337-
identical.*
343+
does not affect identity either. `identical((a: 1, b: 2), (b: 2, a: 1))` is
344+
not required to return false.*
338345
* `false`, if any pair of corresponding fields are not identical.
339-
* Otherwise `true`.
340-
341-
This means `identical()` on records is structural and recursive. However, since
342-
records are immutable and `identical()` on other aggregate types does not
343-
recurse into fields, it cannot be *cyclic.*
344-
345-
An important use case for `identical()` is as a fast path check for equality.
346-
It's common to use `identical()` to quickly see if two objects are "the same",
347-
and if so avoid the potentially slower call to `==`. We have some concern that
348-
structural rules for `identical()` of records could be slow.
349-
350-
We will coordinate with the implementation teams and if they are not confident
351-
that they can get reasonable performance out of it, we may change these rules
352-
before accepting the proposal. "Reasonable" here means fast enough that users
353-
won't find themselves wishing for some other specialized `reallyIdentical()`
354-
function that avoids the cost of structural `identical()` checks on records.
355-
356-
**TODO: Discuss with implementation teams.**
346+
* Otherwise it *may* return `true`, but is not required to.
347+
348+
*If an implementation can easily determine that two record arguments to
349+
`identical()` have the same shape and identical fields, then it should return
350+
`true`. Typically, this is because the two arguments to `identical()` are
351+
pointers with the same address to the same heap-allocated record object. But if
352+
an implementation would have to do a slower field-wise comparison to determine
353+
identity, it's probably better to return `false` quickly.*
354+
355+
*In other words, if `identical()` returns `true`, then the records are
356+
definitely indistinguishable. But if it returns `false`, they may or may not
357+
be.*
357358

358359
#### Expandos
359360

0 commit comments

Comments
 (0)