Given the following function, entered at the REPL:
(defun animalp (thing)
(if (member thing '(dog cat snail mouse)) t))
It is fairly simple to ask:
(animalp 'dog)
Things get more complicated after splitting into packages:
(in-package :common-lisp-user)
(defpackage :animalia
(:use :common-lisp)
(:export :animalp))
(in-package :animalia)
(defun animalp (thing)
(if (member thing '(dog cat snail mouse)) t))
Now, one can still ask the same question:
(animalia:animalp 'animalia::dog)
But it is getting messy. (I'm not fussed about "animalia:animalp", it's the large number of animals that is problematic.) Essentially I'd like to be able to ask:
(animalia:animalp 'dog)
The symbols (dog, cat, etc) don't have any properties - essentially I've been using them as a shorthand for string comparisons:
(if (member "dog" '("dog" "cat" "mouse" "snail")
:test #'string-equal) t)
So my question is around best practice. I'm not enamoured with using strings - especially when (eq 'dog 'dog) is so simple within a single package. On the other hand, neither am I overjoyed with the prospect of (defpackage ... (:export :dog :cat ...)) and needing to qualify each animal with a package (animialia:dog, etc). The final obvious solution is to make all the animals keywords, like:
(if (member :dog '(:dog :cat :mouse :snail)) t)
but even that just seems a little dirty.
What are some best practice solutions to achieve what I want, without making a complete mess or resorting to the evolution of fanciful, ugly and potential fragile solutions?