Why are there so many ways to compare for equality?

common-lisp, comparison, equality, lisp

Solution

You actually can compare everything with `eq`, `eql`, `equal` and `equalp`. But choose for yourself, what's needed:

It's easy to think that `eq` compare pointers. Thus symbols (because a unique symbol enters package only once and its address is the same in every case you use it) will return true.

CL-USER> (eq 'a 'a)
T

Next, different lists, even if contain the same values, are not equal (in terms of `eq`). They have different addresses, they are different objects.

CL-USER> (defparameter *a* '(1 2))
*A*
CL-USER> (defparameter *b* '(1 2))
*B*
CL-USER> (eq *a* *b*)
NIL

But it's not always true, for example if you're dealing with literal object:

CL-USER> (lambda nil '(1 2 3))
#<FUNCTION :LAMBDA NIL '(1 2 3)>
CL-USER> (eq * *)
T

While:

CL-USER> (eq '(1 2 3) '(1 2 3))
NIL

Tricky? You may expect that values of different variables that contain the same numbers will not be equal, since the numbers should occupy different blocks of memory, but...

CL-USER> (defparameter *c* 5)
*C*
CL-USER> (defparameter *d* 5)
*D*
CL-USER> (eq *c* *d*)
T

Description of `eq` function says about identical objects, so number five is the same object everywhere.

Next, `eql` function is defined upon `eq`, it evaluates `t` in the following cases:

- If x and y are `eq`.

- If x and y are both numbers of the same type and the same value.

- If they are both characters that represent the same character.

And `equal`, `equalp` deal with 'visually similar' objects.

Why so many of them? Think for yourself: in language like LISP where you do not work with pointers explicitly you may want to find exactly the same object (at the same address) in list or anywhere.. So you need function that acts on this 'pointer' level. Next it's obvious that you need 'normal' comparison functions and here they are. Differences are subtle and somewhat historic, but it's often useful to have them all.

It's also important to note that some of these 4 base comparison functions have additional meaning being applied to certain types of data. For example `equal` works for comparison of strings in case-sensitive manner, while `equalp` in case-insensitive one.

CL-USER> (equal "My String" "My String")
T
CL-USER> (equal "My String" "My string")
NIL
CL-USER> (equalp "My String" "My string")
T

To continue with the string example, here are many functions like `string=`, etc. Why? That's what 'The Common Lisp Cookbook' says:

You'll want to use these if you're deploying implementation-defined attributes of characters. Check your vendor's documentation in this case.

However, in vast majority of cases the 4 basic functions will be enough.

Problem

If I want to compare two values for equality there are a number of options, such as: - `eq` for symbols - `=` for numbers - `char-equal` for characters - `string-equal` for strings - `eql` for symbols, numbers and strings - `equal` for everything but symbols - … (And I hope I got this right so far.) Now, as a Lisp beginner, my question is: Why is that? Is this just for "historical reasons" or is there a real benefit of having all these possibilities? I know that why-questions are always hard to answer and may be opinion-based, but I think (guess) that there are best practices that are commonly agreed on by advanced Lisp developers.

Original source

Related problems