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.
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.
There are so many because there are specialized comparison functions that are optimized for certain types. Using them might give you better performance.
This simple rule I read in Land of Lisp
- Use
eq
to compare identify the same object (pointer equal)
- Use
equal
for everything that looks the same
Now sometimes you want to test if a string is equal case insensitively and then equal
wouldn't do that since "test" and "TEST" don't look the same. string-equal
would be the right choice.
When comparing different types of number, like 3.0 and 3 they don't look the same. However if you do (= 3 3.0)
it's T
since they represent the same value if the fixnum was casted down to a float.
Also equalp
is like equal
except it does string case insensitive and numbers like =
. I'm not entirely sure but in my mind a third rule would be:
- Use
equalp
for everything that means the same
So you'll come pretty far with just those 3 generic ones but some cases you might need to search CLHS for a specialized equality operator.
There are basically two sets of comparisons:
- general purpose: EQ (pointer), EQL (pointer + number + char), EQUAL, EQUALP
- type specific: string-equal, string=, ...
The general purpose comparisons try to provide functionality for basic use cases in typical (mostly) symbolic programming.
The type specific comparisons are thought to express (and possibly check) that the arguments are of some type. They also provide efficient implementations. They were added so that these functions can be used in code, which needs to expand into or call specific and efficient functions.