tags:

views:

50

answers:

2

Examples of what you can do.

(defmethod some-fn ((num real))
   (print "an integer"))
(defmethod some-fn ((num real))
  (print "a real"))
(defmethod some-fn ((num (eql 0)))
  (print "zero"))

(some-fn 19323923198319)
"an integer"
(some-fn 19323923198319.3)
"a real"
(some-fn 0)
"zero" 

It also works with a general 'string type.

(defmethod some-fn ((num string))
  (print "a string"))
(some-fn "asrt")
"a string"

Not with a specific string, however

(defmethod some-fn ((num (eql "A")))
  (print "a specifict string")))     
(some-fn "A")
  => "A string"

I imagine it doesn't work because eql does not work on strings in the way that would be necessary for it to work.

(eql "a" "a") => nil

Is there a way to do it?

+3  A: 

Unfortunately not as far as I know. The eql specializer uses only eql for comparison (which is essentially a pointer comparison for strings). In order to compare the contents of strings, you'd need an equal (or equalp) specializer, which does not exist.

If you have only a very small set of strings you need to specialize on, you might consider using keywords:

(defmethod operation ((arg (eql ':tag-1))) ...)
(defmethod operation ((arg (eql ':tag-2))) ...)

which you would call with

(operation (intern ... :keyword))
Dirk
There's no need to quote keywords, they evaluate to themselves.
Vatine
@Vatine: It's a habit. I often even quote `nil` (or `()`) depending on the use (for example, in `:initform`s of a `defclass` or `defstruct`.) Should I seek professional help? :-)
Dirk
+3  A: 

Short answer: Yes, it has.

Long answer:

You wrote:

(defmethod some-fn ((num (eql "A")) (print "a specifict string")))
=> doesn't compile

That's because you got the syntax wrong. It should be:

(defmethod some-fn ((num (eql "A"))) (print "a specific string"))
=> does compile

Which is usually formatted as:

(defmethod some-fn ((num (eql "A")))
  (print "a specifict string"))

If you format it this way and use the built-in indentation tool of your favorite editor, you would see that the indentation looks wrong for your code:

(defmethod some-fn ((num (eql "A"))
                    (print "a specifict string")))

It also may help to try to understand the error message shown by the compiler.

Back to the topic:

You can use strings as any other Lisp object for EQL dispatch in CLOS.

It is just that there are many possible strings that look like "A" and EQL compares for identity (with an exception for numbers and characters). EQL does not compare strings by their characters.

Typically (EQL "A" "A") returns NIL. (Side note: Actually in code compiled by the compiler this expression theoretically can be T. Because the compiler is allowed to reuse data objects to save space in the compiled code. Here we have literal strings, data objects.)

If you enter on the command line

(some-fn "A")

it won't trigger the EQL dispatch.

But this works as expected:

(defparameter *a-string* "A")

(defmethod some-fn ((num (eql *a-string*)))
  (print "a specific string")))

and then

(some-fn *a-string*)

You need to make sure that the variable has a value. The variable is evaluated, when the macro expansion of the DEFMETHOD form is evaluated. The value then is the object that is used for EQL dispatch.

As Dirk has mentioned in his answer, one can use symbols. The purpose of symbols is that (EQL '|A| '|A|) is usually T. Symbols are made EQ during the reading process.

Summary:

EQL dispatch over strings works in CLOS. For practical use, you need to call the function with the same, in terms of EQL, string.

Rainer Joswig
Ops. Didn't even notice the syntax error. Good catch. Technically, using the same string instance (as opposed to "a string with the same contents") amounts to doing some kind of interning/unifying manually, if your strings come from external sources (say, read from a file or somesuch) which might render using plain strings here impractical.
Dirk
Yup, I copied it wrong. I'm using emacs etc...This was a question from a friend that I could not answer. I like the use of the defparam to fix it. Is that entirely portable?
mhb
@mhb : yes, it should be portable
Rainer Joswig