Use a variable to define an optional argument - emacs

I do not know what I am doing or what the terminology is. I only know that I want to use a variable (?) to avoid repeating a value in several places. But I am unable to use the value (?) of that variable (?) to pass it to a optional (keyword?) argument in emacs lisp.
Using literal values is working:
(setq
org-publish-project-alist
'(
("my-notes"
:base-directory "projects/notes"
:base-extension "org"
:publishing-directory "html"
...
)
))
Using variables is not working:
(setq my-base-directory "~/projects/notes")
(setq my-publising-directory "html")
(setq
org-publish-project-alist
'(
("my-notes"
:base-directory my-base-directory
:base-extension "org"
:publishing-directory my-publishing-directory
...
)
))
I get an error:
byte-code: Wrong type argument: stringp, my-base-directory
This is also not working:
(let (
(my-base-directory "~/projects/notes")
(my-publising-directory "html")
)
(setq
org-publish-project-alist
'(
("my-notes"
:base-directory my-base-directory
:base-extension "org"
:publishing-directory my-publishing-directory
...
)
)))
I wonder why it is so difficult to access variables in emacs? Those variables are defined (I can print them), but they can not be accessed for optional arguments (are those things with a colon in front called optional or keyword arguments?)
I have tried a myriad other variations, including (quote ...) and using the quote symbol ':
(setq
org-publish-project-alist
'(
("my-notes"
:base-directory 'my-base-directory
:base-extension "org"
:publishing-directory 'my-publishing-directory
...
)
))
In this case I am getting:
byte-code: Wrong type argument: stringp, (quote my-base-directory)
I just need to use the variable to set the optional argument. How can I do this? Do I need to use a different syntax for optional arguments than in normal use?

I see you've already got a working answer, but have expressed some confusion in your comments; I'll try then to explain it more thoroughly.
When you quote something (either by using (quote foo) or 'foo), you are telling the lisp interpreter not to evaluate foo. This is usually used to input data instead of code; in this case you are creating a moderately complicated list structure to be used as data, rather than as code to be evaluated.
Your code had '(("my-notes" :base-directory my-base-directory)), which is a quoted list of lists. The inner list will contain the keyword :base-directory and the symbol my-base-directory. It doesn't contain the value of the my-base-directory variable because the quote at the beginning has told lisp not to evaluate any of it.
The two ways of getting around this both involve not quoting the whole expression.
The first and simplest option is to use list; this is a function which takes any number of arguments and returns a list containing those values. For example:
(list 1 2 3) => '(1 2 3)
(let ((x 42)) (list x x)) => '(42 42)
Thus, you could structure your code like this:
(setq org-publish-project-alist
(list (list "my-notes" :base-directory my-base-directory
:base-extension "org"
:publishing-directory my-publishing-directory)))
You'll see that you have to call the list function twice, once for each level of the structure. This can get tedious as the structure becomes more complex; quoting was invented to save you this tedium.
(Note also that keywords and strings evaluate to themselves, so they don't need any special treatment when used as arguments rather than members of quoted lists.)
The other option is to use a backquote (or syntax quote as it is occasionally called) instead of a normal quote. The backquote is unusual in that it can be undone using the comma operator:
(setq org-publish-project-alist
`(("my-notes" :base-directory ,my-base-directory
:base-extension "org"
:publishing-directory ,my-publishing-directory)))
The backquote tells lisp not to evaluate the expression, just like the normal quote, but later on the commas let you change your mind and have lisp evaluate some expressions after all.
Backquotes are a bit like Python's string interpolation, except that they apply to the entirety of lisp syntax rather than just to double-quoted strings.
Also, you can put arbitrary lisp expressions after a comma, just like you can put arbitrary lisp expressions after a quote or backquote:
`(("my-notes" :base-directory ,(concat my-projects-directory "notes")))
Note especially that `(("my-notes" :base-directory, my-base-directory)) won't work; one of your comments indicates that you probably tried something like this. This use of the comma is indeed quite foreign to programmers coming from other languages! You are not alone in thinking that this is a very strange way to write things, but after a while it does start to make sense. It helps to think of the comma as a sort of upside-down backquote.

Examples:
Backquoted list:
ELISP> (let ((foo "Foo")) `(,foo bar))
("Foo" bar)
Function LIST:
ELISP> (let ((foo "Foo")) (list foo 'bar))
("Foo" bar)
Example:
(setq
org-publish-project-alist
`(("my-notes"
:base-directory ,my-base-directory
:base-extension "org"
:publishing-directory ,my-publishing-directory
; ...
)))

Related

Backquote expansion in Lisp

I'm a Lisp beginner and I'm struggling to understand why the following code gives me an error.
(dolist (elem '(mapcar
mapcon))
(when (fboundp `',elem) (print "hello")))
Thanks.
Edit:
A bit more context. I wrote the following in Elisp and I don't know how to fix it.
(dolist (ui-elem '(menu-bar-mode
tool-bar-mode
tooltip-mode
scroll-bar-mode
horizontal-scroll-bar-mode))
(when (fboundp `',ui-elem) (ui-elem -1)))
Note
In your question you mix common-lisp and elisp, but they are two different languages. The question however touches on concepts that are identical in both languages.
The need to quote symbols
The code you want to write checks if a symbol is bound to a function.
What you already know probably is that you can call fboundp on a symbol to determines this:
(fboundp 'menu-bar-mode)
=> t
When you evalute the above form, 'menu-bar-mode is the same as (quote menu-bar-mode), and is evaluated as the symbol object menu-bar-mode. This is the value that is given as an argument to fboundp.
In you example you want to iterate over a list of symbols, call fboundp on it and call the function if the symbol denotes a function. You can do this as follows:
(dolist (s '(menu-bar-mode and other symbols))
(when (fboundp s)
(funcall s -1)))
The list of symbols '(menu-bar-mode and other symbols) is quoted, which means that when dolist evaluates it, it sees a list of symbols. The value to which s is bound at each iteration of the loop is a symbol object, there is no need to quote them.
Quoting a symbol is something you have to do when writing them in your code so that they are not interpreted as variables. When you iterate over a list of symbols, you already manipulate symbols.
Note also that both Common Lisp and Emacs Lisp are "Lisp-2", meanings that you have to use (funcall ui-elem -1) instead of writing (ui-elem -1). When you write the latter form, that means calling the function literally named ui-elem because for function application, the first symbol in the list is not evaluated, it is taken literally.
Too many levels of quoting
The actual error I have when I execute your code is:
(wrong-type-argument symbolp 'mapcar)
It may look like 'mapcar denotes a symbol, because when you want the interpreter to evaluate some code as a symbol, you need to quote it. However, Lisp printers write objects in a way that they can be read back to "similar" objects. The error message that is printed if I expect a symbol to be a number is the following, where symbol foo is printed unquoted:
(+ 'foo 3)
;; error: (wrong-type-argument number-or-marker-p foo)
In your error message, the form that you are trying to use as a symbol is (quote mapcar). Recall that when you directly call fboundp:
(fboundp 'mapcar)
It is the same as-if you wrote:
(fboundp (quote mapcar))
First, (quote mapcar) is evaluated, as the symbol mapcar. Then, fboundp is applied to that value.
But when you write the following, while ui-elem is bound to symbol mapcar:
(fboundp `',ui-elem)
This is equivalent to:
(fboundp `(quote ,ui-elem))
The argument to fboundp is evaluated as (quote mapcar). You have one extra level of quoting. You could write instead:
(fboundp `,ui-elem)
But then, you don't need to use backquote/comma, you can directly write:
(fboundp ui-elem)

pass a list to macro in Common Lisp

I'm having a problem passing a list to macro, where the list will be used to generate function name. For example, the code below causes an error.
(defmacro gen (str-lst)
`(defun ,(intern (string-upcase (car str-lst))) () (print "foo")))
(gen '("foo" "bar"))
The resulting error was:
*** - DEFUN/DEFMACRO: QUOTE is a special operator and may not be redefined. The following restarts are available: ABORT :R1
Abort main loop
How should I modify my code, and what is wrong with my code?
The thing makes me even more confused is that the code below, about which answer exits here, works fine.
(defmacro easy-one (str-lst)
`(mapc #'(lambda (str) (print str)) ,str-lst))
(easy-one '("foo" "bar"))
Don't quote the list. Macros don't evaluate their arguments, so you don't need to quote them to prevent them from being evaluated, like you do for ordinary functions.
(gen ("foo" "bar"))
When you quote it, you're executing
(get (quote ("foo" "bar")))
The value of str-list is the list (quote ("foo" "bar")), so (car str-list) is the symbol QUOTE. As a result, the macro expands to
(defun quote () (print "foo"))
That's why you get an error complaining that you're trying to redefine the built-in QUOTE.
The difference in your second example is that you're just substituting the parameter into the expansion, you're not using its value in the expansion code. So it expands to
(mapc #'(lambda (str) (print str)) '("foo" "bar")))
Here the list will be used when the expansion runs, not while the macro is being expanded. It needs to be quoted there, to prevent it from being evaluated as a function call.
You should use macroexpand to see how your macros are being expanded when debugging.

make-symbol added unwanted formatting to generated symbols

I'm trying to write a function primeify that accepts a symbol and returns the symbol with "-prime" appended to it. My desired output is:
[1] > (primeify 'y)
Y-PRIME
(or y-prime, the case isn't the main point here, although it may become relevant later).
Here's my current (erroneous) implementation:
(defun primeify (sym)
(make-symbol (concatenate 'string (string sym) "-prime")))
However, make-symbol is mangling the output by cluttering it with additional formatting. My output is:
[1]> (primeify 'y)
#:|Y-prime|
Is there any way to avoid this additional processing done by make-symbol, or another function I could use to accomplish this? Is this even possible to accomplish in lisp?
Your symbol:
#:|Y-prime|
This is a non-interned symbol. It is in no package. #: is in front of the symbol because of that.
It is also a symbol name with mixed case. Because of that it is escaped with vertical bars. Remember, by default symbol names are internally stored in UPPERCASE.
(defun primeify (sym)
(let ((name (concatenate 'string
(string sym)
"-PRIME")))
(if (symbol-package sym)
(intern name (symbol-package sym))
(make-symbol name))))
Above function gives the new symbol the same package as the original symbol has, if any.
CL-USER 3 > (primeify 'foo)
FOO-PRIME
NIL
CL-USER 4 > (primeify '#:foo)
#:FOO-PRIME

How do I define a function that creates a function alias?

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.
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.)
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.
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))

Elisp, alist and strings; type confusion

I'm trying to publish an org-project as html, and automate the task with the following org project definition:
(defconst home (file-name-directory (or load-file-name buffer-file-name)))
(require 'org-publish)
(setq org-publish-project-alist
'(
;; add all the components here
;; *notes* - publishes org files to html
("org-notes"
:base-directory (concat home "org/")
:base-extension "org" ; Filename suffix without dot
:publishing-directory (concat home "../public_html/")
:recursive t ; includes subdirectories
:publishing-function org-publish-org-to-html
:headline-levels 4 ; Just the default for this project.
:auto-preamble t
:auto-sitemap t ; generate automagically
:sitemap-filename "sitemap.org"
:sitemap-title "Sitemap"
)
;; *static* - copies files to directories
("org-static"
:base-directory (concat home "org/")
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
:publishing-directory (concat home "../public_html/")
:recursive t
:publishing-function org-publish-attachment
)
;; *publish* with M-x org-publish-project RET emacsclub RET
("emacsclub" :components ("org-notes" "org-static"))
))
However, upon exporting the project, I get the error
Wrong type argument: stringp, (concat home "org")
From an Elisp perspective, what the heck is going on? Isn't the output of concat a string? In which case why is this failing? I try stringp on it's own with the concat argument and it returns true.
Something else I'm trying to get done, is have the whole project exported when this file gets evaluated. I've tried things like
(command-execute org-publish-all)
but it also complains of a wrong type argument. What can I use to get this done?
The problem is that quoting the second argument in (setq org-publish-project-alist '(...)) means that nothing in that list structure will be evaluated. In other words, Emacs is telling you that the value (concat home "org") isn't a string: in fact it's a list of three elements (which if evaluated would give you a string).
One possible workaround might be to use the "backquote" or "quasiquote" mechanism, which is just like quote or ' but allows you to selectively splice in bits of evaluated Lisp code using , and ,#. (See (elisp)Backquote in the Info manual for more details). So, you could change your code above to something like
(setq org-publish-project-alist
`( ; note ` instead of '
("org-notes"
;; Note commas , in front of code to evaluate
:base-directory ,(concat home "org/")
:base-extension "org"
:publishing-directory ,(concat home "../public_html/")
....
Note that the unquoted pieces will get evaluated and spliced in to the list only once, when the (setq ...) form is evaluated: in other words, this won't help you if you need those values to change dynamically for different project directories. But since you're defining home as a constant maybe that doesn't matter?
PS: If you need to figure out in more detail where your wrong-type-argument errors are coming from, try doing M-x toggle-debug-on-error or evaluate (setq debug-on-error t) to get a detailed backtrace.