I export a few symbols from a package and use them internally in a case statement. I'd like to pass one of the symbols (e.g. medium-size) as a keyword argument to a generic method, like this:
(solve problem :optimize-for :medium-size :another-keyword t)
but this gives an "odd number of &KEY arguments" error. Am I misunderstanding something fundamental about symbols or keyword arguments?
That is unexpected behaviour, if solve has been defined in a manner similar to:
(defun solve (problem &key optimize-for another-keyword)
...)
However, if you have exported the symbol medium-size from your-package, it would normally be referred to as 'your-package:medium-size (or, if you have imported your-package, just 'medium-size).
Related
*Note: Despite having frequented StackOverflow for a long time, this is the first question that I have posted myself. Apologies if it's a bit verbose. Constructive criticism appreciated.
When I define a struct in Common Lisp using defstruct, a predicate function is automatically generated that tests whether its argument is of the type defined by the defstruct. Eg:
(defstruct book
title
author)
(let ((huck-finn (make-book :title "The Adventures of Huckleberry Finn" :author "Mark Twain")))
(book-p huck-finn))
=> True
However, when defining a class using defclass, such functions are seemingly not generated by default (is there a way to specify this?), so I'm trying to add this functionality myself, because I'd like a) for this syntax to be consistent between structs and classes, b) to have an abbreviation of (typep obj 'classname), which I need to write very often and is visually noisy,
and c) as a programming exercise, since I'm still relatively new to Lisp.
I could write a macro that defines a predicate function given the name of a class:
(defclass book ()
((title :initarg :title
:accessor title)
(author :initarg :author
:accessor author)))
;This...
(defmacro gen-predicate (classname)
...)
;...should expand to this...
(defun book-p (obj)
(typep obj 'book))
;...when called like this:
(gen-predicate 'book)
The name that I need to pass to defun must be of the form 'classname-p. Here's where I have difficulty. To create such a symbol, I could use the "symb" function from Paul Graham's On Lisp (p. 58). When it is run on the REPL:
(symb 'book '-p)
=> BOOK-P
My gen-predicate macro looks like this so far:
(defmacro gen-predicate (classname)
`(defun ,(symb classname '-p) (obj)
(typep obj ,classname)))
(macroexpand `(gen-predicate 'book))
=>
(PROGN
(EVAL-WHEN (:COMPILE-TOPLEVEL) (SB-C:%COMPILER-DEFUN '|'BOOK-P| 'NIL T))
(SB-IMPL::%DEFUN '|'BOOK-P|
(SB-INT:NAMED-LAMBDA |'BOOK-P|
(OBJ)
(BLOCK |'BOOK-P| (TYPEP OBJ 'BOOK)))
NIL 'NIL (SB-C:SOURCE-LOCATION)))
T
It would seem that the symbol created by (symb 'book '-p) is actually considered |'BOOK-P| by the implementation (SBCL), not BOOK-P. Sure enough, this now works:
(let ((huck-finn (make-instance 'book)))
(|'BOOK-P| huck-finn))
=> True
Why is the symbol created by symb interned as |'BOOK-P|? In On Lisp (same page as above) Graham says: "Any string can be the print-name of a symbol, even a string containing lowercase letters or macro characters like parentheses. When a symbol's name contains such oddities, it is printed within vertical bars." No such oddities exist in this case, do they? And am I correct in thinking that the "print-name" of a symbol is what is actually displayed on the standard output when the symbol is printed, and is, in the case of such oddities, distinct from the form of the symbol itself?
To be able to write function-defining macros like gen-predicate - whose defined functions are named based on the arguments passed to the macro - seems to me like something that Lisp hackers have probably been doing for ages. User Kaz says here (Merging symbols in common lisp) that the "mashing-up" of symbols can often be avoided, but that would defeat the purpose of this macro.
Finally, assuming I could get gen-predicate to work how I want, what would be the best way of ensuring that it be called for each new class as they are defined? Much in the same way as initialize-instance can be customized to perform certain actions upon instantiation of a class, is there a generic function called by defclass that can perform actions upon definition of a class?
Thank you.
That's a usual problem: what gets passed to a Macro?
Compare calls like this:
(symb 'book '-p)
and
(symb ''book '-p)
Your macro form is this:
(gen-predicate 'book)
GEN-PREDICATE is a macro. classname is a parameter for this macro.
Now what is the value of classname inside the macro during code expansion? Is it book or 'book?
Actually it is the latter, because you wrote (gen-predicate 'book). Remember: macros see source code and the argument source gets passed to the macro function - not the value. The argument is 'book. Thus this gets passed. (QUOTE BOOK) is the same, only printed differently. So it is a two element list. The first element is the symbol QUOTE and the second element is the symbol BOOK.
Thus the macro now calls the function SYMB with the argument value (QUOTE BOOK) or, shorter, 'BOOK.
If you want to generate the predicate without the quote character, you need to write:
(gen-predicate book)
Alternatively you can also change the macro:
(symb classname '-p)
would be:
(symbol (if (and (consp classname)
(eq (first classname) 'quote))
(second classname)
classname))
Compare
We write
(defun foo () 'bar)
and not
(defun 'foo () 'bar) ; note the quoted FOO
DEFUN is a macro and the first argument is the function name. It's a similar problem then...
Second part of the question
I don't really know any good answer to that. I can't remember any easy way to run code (for example to define a function) after a class definition.
Maybe use the MOP, but that's ugly.
write a custom macro DEFINE-CLASS which does what you want: expands into DEFCLASS and the DEFUN.
iterate over all symbols in a package, find the classes and define the corresponding predicates
To address the second part of the question, classes are themselves objects, thanks to the MOP, so it might be possible to write an :after method on initialize-instance specialized on STANDARD-CLASS. But you should check the MOP to see whether defining such a method is allowed or not.
If it's possible, then yes, you can run code in response to the creation of a class; however, since you don't know the name of the class being created until runtime, you cannot spell it textually in the source, so you cannot use your macro (unless you use eval). You'd rather use something like
(let ((classname (class-name class)))
(compile (generate-my-predicate-symbol classname)
(lambda (x) (typep x classname))))
I think Rainer's suggestion to write your own DEFINE-CLASS macro is the way to go, I mean, the way a seasoned Lisper most likely would do it, if there aren't any other considerations at play. But I'm not really a seasoned Lisper, so I might be wrong ;)
I have been programming in common lisp for a little while now, and throughout my experience using lisp, I have yet to see any function/macro that acts anything similar to function prototypes in C or C++.
Currently I have to very careful about the ordering of my functions, otherwise, when I try to call a function from another, Lisp says the function "does not exist" because it is defined later in the file. Is there a way to get around this? Can I declare all my function prototypes at the top of the file, and the full definitions below?
Declaim and Proclaim
You can use declaim to globally declare that a certain thing has a certain function type. For instance, look at what happens first if you define foo1 that calls undefined baz (in SBCL):
CL-USER> (defun foo1 ()
(baz))
; in: DEFUN FOO1
; (BAZ)
;
; caught STYLE-WARNING:
; undefined function: BAZ
;
; compilation unit finished
; Undefined function:
; BAZ
; caught 1 STYLE-WARNING condition
FOO1
Now, let's add a declamation that says that baz is a function of no arguments, and returns something. You could obviously add more type information if you wanted to, but this will at least provide the arity and the knowledge that baz is a function.
CL-USER> (declaim (ftype (function () t) baz))
; No value
Now when you define foo2 that also calls baz, you'll get no warning:
CL-USER> (defun foo2 ()
(baz))
FOO2
Declaim is a macro, but if you need to be able to generate some of these things at runtime, you can use proclaim, which is a function. E.g.,
CL-USER> (dolist (f '(square cube))
(proclaim `(ftype (function (number) number) ,f)))
NIL
CL-USER> (defun add-square-and-cube (x y)
(+ (square x) (cube y)))
ADD-SQUARE-AND-CUBE
That said, this is not very idiomatic Common Lisp. It's much more common to put the code you need into a file and then to compile that file and load it. If that's not possible for some reason, this will work, but it's worth considering other options of loading your code if they're available.
Muffling warnings
It's also worth noting that while SBCL will take the hint from proclaim or declaim and silence the undefined function warning, the function is still actually undefined. Other implementations (e.g., CLISP) will still issue a warning about the undefined function.
I don't really recommend the following approach, because warnings are around for a reason, but you can choose to muffle warnings when you evaluate code. E.g., in CLISP, we get a warning when we compile with undefined functions:
CL-USER> (compile nil (lambda () (baz)))
WARNING: Function BAZ is not defined
#<COMPILED-FUNCTION NIL>
1
1
We can bind a handler that will muffle any warnings that occur when the form is evaluated, though:
CL-USER> (handler-bind ((warning
(lambda (x)
(muffle-warning x))))
(compile nil (lambda () (baz))))
#<COMPILED-FUNCTION NIL>
1
1
This has its ow caveats, too, since the type of warning that you might get for compiling a reference to an undefined function might vary, and what muffling the warning does may vary.
As Rainer pointed out you can ignore this issue if the forward reference is within a single compilation unit. But that won't help if the forward reference crosses compilation units. Usually that's a sign that your code is poorly layered. Low level code makes calls to higher level code? Well, people will say the low level code is providing a hook for the high level code.
That said I certainly have seen and written code that had this problem. Spaghetti code, yum! It can arise when you start breaking huge source files into smaller ones. A compilation unit is usually a single file. But take a look at with-compilation-unit. I don't recall asdf doesn't provide easy access that though.
I don't know if Joshua's solution of using providing a declaration will work across all CL implementations. My memory is that when I had to solve this problem many many years ago we had to implement something more crude, we would give the function a stand in definition, and then hack together a way to suppress the definition warnings.
No doubt cl-launch could be used to see if Joshua's solution works across the spectrum of implementations.
You can define something to be a generic function before you define any of its methods. So, if you write
(defgeneric foo (x y z))
then any function calling FOO will see a defined function (although one without any methods). The body of the function can then be added later as a method
(defmethod foo (x y z)
;; The body of the function
...)
I have been programming in common lisp for a little while now, and throughout my experience using lisp, I have yet to see any function/macro that acts anything similar to function prototypes in C or C++.
Currently I have to very careful about the ordering of my functions, otherwise, when I try to call a function from another, Lisp says the function "does not exist" because it is defined later in the file. Is there a way to get around this? Can I declare all my function prototypes at the top of the file, and the full definitions below?
Declaim and Proclaim
You can use declaim to globally declare that a certain thing has a certain function type. For instance, look at what happens first if you define foo1 that calls undefined baz (in SBCL):
CL-USER> (defun foo1 ()
(baz))
; in: DEFUN FOO1
; (BAZ)
;
; caught STYLE-WARNING:
; undefined function: BAZ
;
; compilation unit finished
; Undefined function:
; BAZ
; caught 1 STYLE-WARNING condition
FOO1
Now, let's add a declamation that says that baz is a function of no arguments, and returns something. You could obviously add more type information if you wanted to, but this will at least provide the arity and the knowledge that baz is a function.
CL-USER> (declaim (ftype (function () t) baz))
; No value
Now when you define foo2 that also calls baz, you'll get no warning:
CL-USER> (defun foo2 ()
(baz))
FOO2
Declaim is a macro, but if you need to be able to generate some of these things at runtime, you can use proclaim, which is a function. E.g.,
CL-USER> (dolist (f '(square cube))
(proclaim `(ftype (function (number) number) ,f)))
NIL
CL-USER> (defun add-square-and-cube (x y)
(+ (square x) (cube y)))
ADD-SQUARE-AND-CUBE
That said, this is not very idiomatic Common Lisp. It's much more common to put the code you need into a file and then to compile that file and load it. If that's not possible for some reason, this will work, but it's worth considering other options of loading your code if they're available.
Muffling warnings
It's also worth noting that while SBCL will take the hint from proclaim or declaim and silence the undefined function warning, the function is still actually undefined. Other implementations (e.g., CLISP) will still issue a warning about the undefined function.
I don't really recommend the following approach, because warnings are around for a reason, but you can choose to muffle warnings when you evaluate code. E.g., in CLISP, we get a warning when we compile with undefined functions:
CL-USER> (compile nil (lambda () (baz)))
WARNING: Function BAZ is not defined
#<COMPILED-FUNCTION NIL>
1
1
We can bind a handler that will muffle any warnings that occur when the form is evaluated, though:
CL-USER> (handler-bind ((warning
(lambda (x)
(muffle-warning x))))
(compile nil (lambda () (baz))))
#<COMPILED-FUNCTION NIL>
1
1
This has its ow caveats, too, since the type of warning that you might get for compiling a reference to an undefined function might vary, and what muffling the warning does may vary.
As Rainer pointed out you can ignore this issue if the forward reference is within a single compilation unit. But that won't help if the forward reference crosses compilation units. Usually that's a sign that your code is poorly layered. Low level code makes calls to higher level code? Well, people will say the low level code is providing a hook for the high level code.
That said I certainly have seen and written code that had this problem. Spaghetti code, yum! It can arise when you start breaking huge source files into smaller ones. A compilation unit is usually a single file. But take a look at with-compilation-unit. I don't recall asdf doesn't provide easy access that though.
I don't know if Joshua's solution of using providing a declaration will work across all CL implementations. My memory is that when I had to solve this problem many many years ago we had to implement something more crude, we would give the function a stand in definition, and then hack together a way to suppress the definition warnings.
No doubt cl-launch could be used to see if Joshua's solution works across the spectrum of implementations.
You can define something to be a generic function before you define any of its methods. So, if you write
(defgeneric foo (x y z))
then any function calling FOO will see a defined function (although one without any methods). The body of the function can then be added later as a method
(defmethod foo (x y z)
;; The body of the function
...)
I defined this macro:
(defmacro with-current-directory (directory &rest body)
"Set the working directory temporarily set to DIRECTORY and run BODY.
DIRECTORY is expanded"
`(let ((default-directory
,(file-name-as-directory
(expand-file-name (eval directory)))))
,#body))
which I use in some lisp functions that are loaded when emacs opens. I always get these warnings:
Eager macro-expansion failure: (void-variable repo-dir)
Eager macro-expansion failure: (wrong-type-argument stringp nil)
Which I understand is happening because those variables are not defined at load time, and emacs is trying to eval them. My question is, how do I avoid getting these warnings. Is there a way to define the macro so that doesn't happen? I cannot figure out how to use the value of the variable, and not the symbol for the variable itself.
Like this:
`(let ((default-directory
(file-name-as-directory (expand-file-name ,directory))))
Since directory is not the value, but the lisp expression that would evaluate to the value, you need to insert (using the comma operator) the expression into the expansion of the macro. If you put the comma before the call to file-name-as-directory, you would have to be able to compute the directory at macro expansion time based only on the tokens of the expression, which you can't do if directory refers to a variable name.
Looks like some beat me to it. Look at the other answer.
You should not be evaluating the file name expansion at expansion time. Also the eval call should not be there. The only thing that should happen during macro expansion is placing the value of directory inside the returned expression. Remove eval and place your comma in front of directory.
It's nice to know that anytime you're using eval, you're probably doing something wrong
I'm trying to pass a function as an argument and call that function within another function.
A piece of my code looks like this:
(defun getmove(strategy player board printflag)
(setq move (funcall strategy player board))
(if printflag
(printboard board))
strategy is passed as a symbol represented in a two dimensional list as something such as 'randomstrategy
I keep getting the error:
"FUNCALL: 'RANDOMSTRATEGY is not a function name; try using a symbol instead...
When I replace strategy with 'randomstrategy it works fine.
I can also call randomstrategy independently.
What is the problem?
The problem is that the variable strategy does not contain the symbol randomstrategy but rather the list (!) 'randomstrategy (which is a shorthand notation for (quote randomstrategy)).
Now, you could, of course, extract the symbol from the list by way of the function second, but that would only cover the real problem up, which is probably somewhere up the call chain. Try to determine why the argument that is passed to function getmove is 'randomstrategy, not randomstrategy as it should be. (Maybe you erroneously used a quote inside of a quoted list?)
Oh, and don't let yourself be confused by the fact that (funcall 'randomstrategy ...) works: the expression 'randomstrategy does not, after all, evaluate to itself, but to the symbol randomstrategy.
Is strategy a variable with a functional value? If not, then use the #' syntax macro before it, i.e. #'strategy, or just (if the function is global) 'strategy.
WHY? Because arguments of a funcall call are evaluated. And your strategy symbol is just a variable name in this case. Variable this value 'RANDOMSTRATEGY. But you should give to funcall a function. How to access function if we have a symbol?
Three cases:
Symbol may denote a variable with functional value.
Symbol may denote a global function (symbol-function is the accessor in this case.
Symbol may denote a local function (flet, labels and so on).
It looks like you forgot to define RANDOMSTRATEGY function.
(defun RANDOMSTRATEGY …)
Hmm
FUNCALL: 'RANDOMSTRATEGY
Maybe you have (setq strategy ''RANDOMSTRATEGY)?
Then strategy will evaluate to 'RANDOMSTRATEGY.
Did you notice ' before the symbol name? 'RANDOMSTRATEGY <=> (quote RANDOMSTRATEGY); it is not a proper function name.
Have you set strategy anywhere? It looks like a scoping issue.
Try this
(setq strategy 'randomstrategy)
(setq move (funcall strategy player board))
Not seeing the code, I'm imagining you're doing something like this:
(defun randomstrategy (a b c) ...)
and then doing this:
(getmove 'randomstrategy x y z)
What you want to do is pass the function "randomstrategy" to getmove using #':
(getmove #'randomstrategy x y z)
In CommonLisp, #' yields the function bound to the symbol, which is
what you want to pass to getmove.