Equality is the concept of whether two values are “the same.” Racket supports a few different kinds of equality by default, although equal? is preferred for most uses.
Datatypes with further specification of equal? include strings, byte strings, pairs, mutable pairs, vectors, boxes, hash tables, and inspectable structures. In the last six cases, equality is recursively defined; if both v1 and v2 contain reference cycles, they are equal when the infinite unfoldings of the values would be equal. See also gen:equal+hash and prop:impersonator-of .
#t
#f
#t
#t
#f
#t
#t
#t
#t
#t
procedure
(equal-always? v1v2)→boolean?
v1:any/cv2:any/c
Two values v1 and v2 are equal-always? if and only if there exists a third value v3 such that v1 and v2 are both chaperones of v3, meaning (chaperone-of? v1v3) and (chaperone-of? v2v3) are both true.
For values that include no chaperones or other impersonators, v1 and v2 can be considered equal-always if they are equal? , except that corresponding mutable vectors, boxes, hash tables, strings, byte strings, mutable pairs, and mutable structures within v1 and v2 must be eq? , and equality on structures can be specialized for equal-always? through gen:equal-mode+hash .
#t
#f
#t
#t
#f
#t
#t
#f
#t
#f
#t
#t
Added in version 8.5.0.3 of package base.
The number and character datatypes are the only ones for which eqv? differs from eq? . Two numbers are eqv? when they have the same exactness, precision, and are both equal and non-zero, both +0.0, both +0.0f0, both -0.0, both -0.0f0, both +nan.0, or both +nan.f—considering real and imaginary components separately in the case of complex numbers. Two characters are eqv? when their char->integer results are equal.
Generally, eqv? is identical to equal? except that the former cannot recursively compare the contents of compound data types (such as lists and structs) and cannot be customized by user-defined data types. The use of eqv? is lightly discouraged in favor of equal? .
#t
#f
#t
#t
#f
#t
#f
#t
#f
#t
#t
#f
#t
#f
#f
#t
#f
#t
#f
#t
procedure
( equal?/recur v1v2recur-proc)→boolean?
v1:any/cv2:any/c
#t
#f
#t
procedure
( equal-always?/recur v1v2recur-proc)→boolean?
v1:any/cv2:any/c
#t
#f
#t
The eq? operator compares two values, returning #t when the values refer to the same object. This form of equality is suitable for comparing objects that support imperative update (e.g., to determine that the effect of modifying an object through one reference is visible through another reference). Also, an eq? test evaluates quickly, and eq? -based hashing is more lightweight than equal? -based hashing in hash tables.
In some cases, however, eq? is unsuitable as a comparison operator, because the generation of objects is not clearly defined. In particular, two applications of + to the same two exact integers may or may not produce results that are eq? , although the results are always equal? . Similarly, evaluation of a lambda form typically generates a new procedure object, but it may re-use a procedure object previously generated by the same source lambda form.
The behavior of a datatype with respect to eq? is generally specified with the datatype and its associated procedures.
All comparable values have at least one hash code — an arbitrary integer (more specifically a fixnum) computed by applying a hash function to the value. The defining property of these hash codes is that equal values have equal hash codes. Note that the reverse is not true: two unequal values can still have equal hash codes. Hash codes are useful for various indexing and comparison operations, especially in the implementation of hash tables. See Hash Tables for more information.
procedure
v:any/c
For any v that could be produced by read , if v2 is produced by read for the same input characters, the (equal-hash-code v) is the same as (equal-hash-code v2) — even if v and v2 do not exist at the same time (and therefore could not be compared by calling equal? ).
Changed in version 6.4.0.12 of package base: Strengthened guarantee for read able values.
procedure
( equal-hash-code/recur vrecur-proc)→fixnum?
v:any/c
(cond#t
#f
#t
Added in version 8.8.0.9 of package base.
procedure
v:any/c
procedure
v:any/c
As equal-always-hash-code traverses v, immutable values within v are hashed with equal-hash-code , while mutable values within v are hashed with eq-hash-code .
procedure
( equal-always-hash-code/recur vrecur-proc)→fixnum?
v:any/c
Added in version 8.8.0.9 of package base.
procedure
v:any/c
procedure
(eq-hash-code v)→fixnum?
v:any/c
procedure
(eqv-hash-code v)→fixnum?
v:any/c
value
equal-proc:(-> any/c any/c (-> any/c any/c boolean? )any/c ) — tests whether the first two arguments are equal, where both values are instances of the structure type to which the generic interface is associated (or a subtype of the structure type).
The third argument is an equal? predicate to use for recursive equality checks; use the given predicate instead of equal? to ensure that data cycles are handled properly and to work with equal?/recur (but beware that an arbitrary function can be provided to equal?/recur for recursive checks, which means that arguments provided to the predicate might be exposed to arbitrary code).
The equal-proc is called for a pair of structures only when they are not eq? , and only when they both have a gen:equal+hash value inherited from the same structure type. With this strategy, the order in which equal? receives two structures does not matter. It also means that, by default, a structure sub-type inherits the equality predicate of its parent, if any.
hash-proc:(-> any/c (-> any/c exact-integer? )exact-integer? ) — computes a hash code for the given structure, like equal-hash-code . The first argument is an instance of the structure type (or one of its subtypes) to which the generic interface is associated.
The second argument is an equal-hash-code -like procedure to use for recursive hash-code computation; use the given procedure instead of equal-hash-code to ensure that data cycles are handled properly.
Although the result of hash-proc can be any exact integer, it will be truncated for most purposes to a fixnum (e.g., for the result of equal-hash-code ). Roughly, truncation uses bitwise-and to take the lower bits of the number. Thus, variation in the hash-code computation should be reflected in the fixnum-compatible bits of hash-proc’s result. Consumers of a hash code are expected to use variation within the fixnum range appropriately, and producers are not responsible to reflect variation in hash codes across the full range of bits that fit within a fixnum.
hash2-proc:(-> any/c (-> any/c exact-integer? )exact-integer? ) — computes a secondary hash code for the given structure. This procedure is like hash-proc, but analogous to equal-secondary-hash-code .
Take care to ensure that hash-proc and hash2-proc are consistent with equal-proc. Specifically, hash-proc and hash2-proc should produce the same value for any two structures for which equal-proc produces a true value.
The equal-proc is not only used for equal? , it is also used for equal?/recur , and impersonator-of? . Furthermore, if the structure type has no mutable fields, equal-proc is used for equal-always? , and chaperone-of? . Likewise hash-proc and hash2-proc are used for equal-always-hash-code and equal-always-secondary-hash-code , respectively, when the structure type has no mutable fields. Instances of these methods should follow the guidelines in Honest Custom Equality to implement all of these operations reasonably. In particular, these methods should not access mutable data unless the struct is declared mutable.
When a structure type has no gen:equal+hash or gen:equal-mode+hash implementation, then transparent structures (i.e., structures with an inspector that is controlled by the current inspector) are equal? when they are instances of the same structure type (not counting sub-types), and when they have equal? field values. For transparent structures, equal-hash-code and equal-secondary-hash-code (in the case of no mutable fields) derive hash code using the field values. For a transparent structure type with at least one mutable field, equal-always? is the same as eq? , and an equal-secondary-hash-code result is based only on eq-hash-code . For opaque structure types, equal? is the same as eq? , and equal-hash-code and equal-secondary-hash-code results are based only on eq-hash-code . If a structure has a prop:impersonator-of property, then the prop:impersonator-of property takes precedence over gen:equal+hash if the property value’s procedure returns a non-#f value when applied to the structure.
#f
#f
#t
Changed in version 8.7.0.5 of package base: Added a check so that omitting any of equal-proc, hash-proc, and hash2-proc is now a syntax error.
value
equal-mode-proc:(-> any/c any/c (-> any/c any/c boolean? )boolean? any/c ) — the first two arguments are the values to compare, the third argument is an equality function to use for recursive comparisons, and the last argument is the mode: #t for an equal? or impersonator-of? comparison or #f for an equal-always? or chaperone-of? comparison.
hash-mode-proc:(-> any/c (-> any/c exact-integer? )boolean? exact-integer? ) — the first argument is the value to compute a hash code for, the second argument is a hashing function to use for recursive hashing, and the last argument is the mode: #t for equal? hashing or #f for equal-always? hashing.
The hash-mode-proc implementation is used both for a primary hash code and secondary hash code.
When implementing these methods, follow the guidelines in Honest Custom Equality. In particular, these methods should only access mutable data if the “mode” argument is true to indicate equal? or impersonator-of? .
#:methodsgen:equal-mode+hash#f
#f
#t
#f
#t
Added in version 8.5.0.3 of package base.
Changed in version 8.7.0.5: Added a check so that omitting either
equal-mode-proc or hash-mode-proc
is now a syntax error.
A prop:equal+hash property value is a list of either three procedures (list equal-prochash-prochash2-proc) or two procedures (list equal-mode-prochash-mode-proc):
The three-procedure case corresponds to the procedures of gen:equal-hash:
equal-proc:(-> any/c any/c (-> any/c any/c boolean? )any/c )
hash-proc:(-> any/c (-> any/c exact-integer? )exact-integer? )
hash2-proc:(-> any/c (-> any/c exact-integer? )exact-integer? )
The two-procedure case corresponds to the procedures of gen:equal-mode-hash:
When implementing these methods, follow the guidelines in Honest Custom Equality. In particular, these methods should only access mutable data if the struct is declared mutable or the mode is true.
Changed in version 8.5.0.3 of package base: Added support for two-procedure values to customize equal-always? .
Since the equal-proc or equal-mode-proc is used for more than just equal? , instances of them should follow certain guidelines to make sure that they work correctly for equal-always? , chaperone-of? , and impersonator-of? .
Due to the differences between these operations, avoid calling equal? within them. Instead, use the third argument to “recur” on the pieces, which allows equal?/recur to work properly, lets the other operations behave in their own distinct ways on the pieces, and enables some cycle detection.
good
(rec(fish-sizeself)(fish-sizeother)))
bad
Don’t use the third argument to “recur” on counts of elements. When a data structure cares about discrete numbers, it can use = on those, not equal? or “recur”. Using “recur” on counts is bad when a “recur” argument from equal?/recur is too tolerant on numbers within some range of each other.
good
(rec((tuple-getterself)i)((tuple-getterother)i)))))
bad
(rec((tuple-getterself)i)((tuple-getterother)i)))))
The operations equal? and equal-always? should be symmetric, so equal-proc instances should not change their answer when the arguments swap:
good
(rec(fish-sizeself)(fish-sizeother)))
bad
However, the operations chaperone-of? and impersonator-of? are not symmetric, so when calling the third argument to “recur” on pieces, pass the pieces in the same order they came in:
good
(rec(fish-sizeself)(fish-sizeother)))
bad
(rec(fish-sizeother)(fish-sizeself)))
The operations equal-always? and chaperone-of? shouldn’t change on mutation, so equal-proc instances should not access potentially-mutable data. This includes avoiding string=? , since strings can be mutable. Type-specific equality functions for immutable types, such as symbol=? , are fine.
fine
;symbols are immutable: no problem
bad
;strings can be mutable: accesses mutable data
Declaring a struct as mutable makes equal-always? and chaperone-of? avoid using equal-proc, so equal-proc instances are free to access mutable data if the struct is declared mutable:
good
#:methodsgen:equal+hash(rec(mcell-valueself)(mcell-valueother)))(rec(mcell-valueself))))(rec(mcell-valueself))))])
bad
;not declared mutable,;but represents mutable data anyway#:methodsgen:equal+hash
Another way for a struct to control access to mutable data is by implementing gen:equal-mode+hash instead of gen:equal+hash . When the mode is true, equal-mode-proc instances are free to access mutable data, and when the mode is false, they shouldn’t:
also good
;only accesses mutable data when mode is true#:methodsgen:equal-mode+hash(rec(mcell-valueself)(mcell-valueother))))(rec(mcell-valueself)))
still bad
;accesses mutable data ignoring mode#:methodsgen:equal-mode+hash(rec(mcell-valueself)(mcell-valueother)))(rec(mcell-valueself))))])
Added in version 8.8.0.5 of package base.
procedure
( hash-code-combine hc...)→fixnum?
#:methodsgen:equal+hash(rec(ordered-triple-sndself)(ordered-triple-sndother))(rec(ordered-triple-thdself)(ordered-triple-thdother))))(rec(ordered-triple-fstself))(rec(ordered-triple-sndself))(rec(ordered-triple-thdself))))(rec(ordered-triple-fstself))(rec(ordered-triple-sndself))(rec(ordered-triple-thdself))))])#t
#t
#f
#f
#f
#f
With one argument, (hash-code-combine hc) mixes the hash code so that it isn’t just hc.
#:methodsgen:equal+hash(rec(wrap-valueself)(wrap-valueother)));demonstrates `hash-code-combine` with only one argument;but it's good to combine `(eq-hash-code struct:wrap)` too#t
#t
#f
#f
procedure
( hash-code-combine-unordered hc...)→fixnum?
#:methodsgen:equal+hash(or(rec(flip-triple-rightself)(flip-triple-rightother)))(rec(flip-triple-rightself)(flip-triple-leftother))))))(rec(flip-triple-midself))(rec(flip-triple-leftself))(rec(flip-triple-rightself)))))(rec(flip-triple-midself))(rec(flip-triple-leftself))(rec(flip-triple-rightself)))))])#t
#t
#t
#t
#f
#f
#:methodsgen:equal+hash(or(rec(rotate-triple-paperself)(rotate-triple-paperother))(rec(rotate-triple-scissorsself)(rotate-triple-scissorsother)))(rec(rotate-triple-paperself)(rotate-triple-scissorsother))(rec(rotate-triple-scissorsself)(rotate-triple-rockother)))(rec(rotate-triple-paperself)(rotate-triple-rockother))(rec(rotate-triple-scissorsself)(rotate-triple-paperother)))))#t
#t
#f
#f
#t
#t
procedure
( hash-code-combine* hc...hcs)→fixnum?
procedure
( hash-code-combine-unordered* hc...hcs)→fixnum?