Pergunta

The Lisp forum thread Define macro alias? has an example of creating function alias using a form such as

(setf (symbol-function 'zero?) #'zerop)

This works fine, making zero? a valid predicate. Is it possible to parametrize this form without resorting to macros? I'd like to be able to call the following and have it create function?:

(define-predicate-alias 'functionp)` 

My take was approximately:

(defun defalias (old new)
  (setf (symbol-function (make-symbol new))
    (symbol-function old)))

(defun define-predicate-alias (predicate-function-name)
  (let ((alias (format nil "~A?" (string-right-trim "-pP" predicate-function-name))))
    (defalias predicate-function-name alias)))

(define-predicate-alias 'zerop)

(zero? '())

This fails when trying to call zero? saying

The function COMMON-LISP-USER::ZERO? is undefined.

Foi útil?

Solução 2

Your code makes a symbol, via MAKE-SYMBOL, but you don't put it into a package.

Use the function INTERN to add a symbol to a package.

To expand on Lars' answer, choose the right package. In this case the default might be to use the same package from the aliased function:

About style:

Anything that begins with DEF should actually be a macro. If you have a function, don't use a name beginning with "DEF". If you look at the Common Lisp language, all those are macro. For example: With those defining forms, one would typically expect that they have a side-effect during compilation of files: the compiler gets informed about them. A function can't.

If I put something like this in a file

(define-predicate-alias zerop)

(zero? '())

and then compile the file, I would expect to not see any warnings about an undefined ZERO?. Thus a macro needs to expand (define-predicate-alias 'zerop) into something which makes the new ZERO? known into the compile-time environment.

I would also make the new name the first argument.

Thus use something like MAKE-PREDICATE-ALIAS instead of DEFINE-PREDICATE-ALIAS, for the function.

Outras dicas

make-symbol creates an uninterned symbol. That's why zero? is undefined.

Replace your (make-symbol new) with e.g. (intern new *package*). (Or you may want to think more carefully in which package to intern your new symbol.)

There are already some answers that explain how you can do this, but I'd point out:

Naming conventions, P, and -P

Common Lisp has a naming convention that is mostly adhered to (there are exceptions, even in the standard library), that if a type name is multiple words (contains a -), then its predicate is named with -P suffix, whereas if it doesn't, the suffix is just P. So we'd have keyboardp and lcd-monitor-p. It's good then, that you're using (string-right-trim "-pP" predicate-function-name)), but since the …P and …-P names in the standard, and those generated by, e.g., defstruct, will be using P, not p, you might just use (string-right-trim "-P" predicate-function-name)). Of course, even this has the possible issues with some names (e.g., pop), but I guess that just comes with the territory.

Symbol names, format, and *print-case*

More importantly, using format to create symbol names for subsequent interning is dangerous, because format doesn't always print a symbol's name with the characters in the same case that they actually appear in its name. E.g.,

(let ((*print-case* :downcase))
  (list (intern (symbol-name 'foo))
        (intern (format nil "~A" 'foo))))

;=> (FOO |foo|) ; first symbol has name "FOO", second has name "foo"

You may be better off using string concatenation and extracting symbol names directly. This means you could write code like (this is slightly different use case, since the other questions already explain how you can do what you're trying to do):

(defmacro defpredicate (symbol)
  (flet ((predicate-name (symbol)
           (let* ((name (symbol-name symbol))
                  (suffix (if (find #\- name) "-P" "P")))
             (intern (concatenate 'string name suffix)))))
    `(defun ,(predicate-name symbol) (x)
       (typep x ',symbol)))) ; however you're checking the type
(macroexpand-1 '(defpredicate zero))
;=> (DEFUN ZEROP (X) (TYPEP X 'ZERO))

(macroexpand-1 '(defpredicate lcd-monitor))
;=> (DEFUN LCD-MONITOR-P (X) (TYPEP X 'LCD-MONITOR))               
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top