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
Related
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)
I'm just learning emacs and came across a configuration that demonstrates quite a bit of the functionality I want in my own configuration. It uses init.el as an entry point to an org file that handles the bulk of the configuration with extensive documentation. I am confused by the following function:
(put 'after-save-hook 'safe-local-variable
(lambda (value) (equal value '(org-babel-tangle t))))
What I think I understand is that this puts the value of the lambda expression into the property list of after-save-hook under the property name safe-local-variable, and that a value is safe if, when passed to the safe-local-variable-p function it returns a non-nil value. This lambda then appears to do an equality comparison between value and the list (org-babel-tangle t), so presumably this means that value is safe only when it's equal to the list (org-babel-tangle t)?
What I am having trouble understanding is twofold. First, where is the lambda getting value from? Second, what is this all actually doing? Neither the documentation I could find on after-save-hook nor org-babel-tangle clarified this for me. The author's comments say "Mark safe variables early so that tangling won't break," but I still don't get it.
The anonymous function with argument value gets its arg from the value of after-save-hook.
after-save-hook is a variable. Its value is a list of hooks.
This code puts the anonymous function as the safe-local-variable property value of symbol after-save-hook, so that when after-save-hook is processed as to see if it is a safe local variable, that function is called. The function is called on the current value of the variable, after-save-hook.
See the Elisp manual, node File Local Variables.
The reason for this is that the file DESKTOP.org starts with this line:
# -*- after-save-hook: (org-babel-tangle t); -*-
That is, when you open that file in Emacs, the local value of after-save-hook becomes (org-babel-tangle t). So whenever you save that file, it's going to call the function org-babel-tangle without any arguments, in order to generate a few shell scripts, e.g. scripts/screenshot.region.sh. The value t in a hook variable means that after calling all the functions in the local value of the hook variable, run-hooks is going to look at the global value of after-save-hook and call any functions listed there as well.
Obviously, allowing any file to specify arbitrary Lisp code to be run would be a security hole equaled only by Microsoft Word macros, so by default file-local settings for risky variables are ignored. Since we know this particular value is safe,* we use the safe-local-variable trick you're asking about. As per the documentation:
You can specify safe values for a variable with a
‘safe-local-variable’ property. The property has to be a function of
one argument; any value is safe if the function returns non-‘nil’ given
that value.
So that's what we have here:
(lambda (value) (equal value '(org-babel-tangle t)))
It is a function that takes one argument, and checks that the argument is equal to the specific value we want to allow. safe-local-variable-p is going to call this function on the specified file-local value, and only allow it if the function returns non-nil. Thus, value is going to be the value that is about to be assigned to after-save-hook. We can see that in action in M-x ielm:
*** Welcome to IELM *** Type (describe-mode) for help.
ELISP> (setq my-function (lambda (value) (equal value '(org-babel-tangle t))))
(lambda
(value)
(equal value
'(org-babel-tangle t)))
ELISP> (funcall my-function '(org-babel-tangle t))
t
ELISP> (funcall my-function 'something-else)
nil
* Is this safe, though?... If an attacker can get you to download a specially crafted file and run org-babel-tangle on it, they can overwrite arbitrary files in the file system using the privileges of your user. It's not what's happening in this case, just something to be aware of.
Is it possible to expand a macro at run time in a compiled lisp executable? I would expect it to be impossible as macro expansion can only happen pre-compilation yet when i call macroexpand in compiled code i get output.
A macro is a function that's normally called automatically during compilation or evaluation, and whose return value is then compiled or evaluated in place of the original expression.
But since it's just a function, there's nothing preventing it from being called explicitly during run time as well, and that's what MACROEXPAND and MACROEXPAND-1 do.
It's roughly equivalent to:
(defun macroexpand-1 (form &optional env)
(if (and (listp form) (car form)) ;; list expression
(let ((macfun (macro-function (car form)))
(if macfun
(funcall macfun form env)
form))
form))
(Note that this definition doesn't handle symbol macros or use *MACROEXPAND-HOOK*, to keep it simple.)
It's possible to use EVAL-WHEN when defining the macro to make the macro definition only available in the compilation environment. If you do that, trying to expand at run time will fail.
In Lisp, the terms "run time" and "compile time" are situations in which a particular piece of code is being processed, not absolutes like in some static languages. If we evaluate (compile nil '(lambda ())), this is the compile function's run-time, but the lambda form's compile time: both times are happening at the same time.
The entire language is available in all situations. When you build a self-contained executable, that image contains not only support for expanding macros, but for compiling code. Your Lisp application can call compile-file to compile Lisp source to object form, and load to load the resulting object code.
The process of removing garbage, and unused functionality from a Lisp application image to make it smaller is called "tree shaking". If you don't want the compiler or macro expander in your application, find out whether/how they can be removed with your implementation's tree shaking support.
If I do this:
(message (format "===> %s loaded" (file-name-base load-file-name)))
I get just the basename of the file.
===> 0100-start loaded
If I do this:
(message (format "===> %s loaded" 'load-file-name))
I get:
===> load-file-name loaded
Not really what I want ...
If I do this:
(message (format "===> %s loaded" load-file-name))
I get:
===> nil loaded
No clue what is happening there. I though the variable load-file-name has the value of the module where it is used. And it somehow has it, otherwise I could not get the (file-name-base load-file-name), but used by itself does not work. I probably need to backquote-forward-quote-and-precede-with-a-comma the variable name, but no idea how to do this. Elisp witchery.
What I want to get is:
===> /home/user1/.emacs.d/conf/0100-start.el loaded
How to get the full, absolute path of the lisp module? I though this was the job of load-file-name. How can I display it?
How can I use a variable in elisp?
First of all, message can do formatting, so format is not needed.
Second, when you quote a variable, like 'foo, it is not evaluated, so you are not getting its value.
Third, C-h f file-name-base RET should explain your first output.
Finally, the correct forma inside the file being loaded is
(message "===> %s loaded" load-file-name)
and the reason you see nil there is that it was not evaluated during loading. Please try C-h v load-file-name RET.
Scoping Rules for Variable Binding1:
Local bindings in Emacs Lisp have indefinite scope and dynamic extent.
[...]
Indefinite scope means that any part of the program can potentially access the variable binding. Extent refers to when, as the program is executing, the binding exists.
Dynamic extent means that the binding lasts as long as the activation of the construct that established it.
(Emacs can also perform lexical binding)
When you load a file, something like this happens:
(defun load (file)
(let ((load-file-name file))
(do-load file)))
And so, load-file-name is only bound to file during the time the file is being loaded. When the code exists the scope of the let, the binding is no more effective.
Note that if you did not load but require a module, then upon loading Emacs will also register the module as being provided and will not reload it when calling require another time. This might be the reason you get nil the third time (see unload-feature).
In your module, you can define a global binding for your own symbol:
(defvar jeckyll2hide/module-path load-file-name)
Then, the first time you load (or require) your module, you will define a variable that will be globally bound to the value that is locally bound to load-file-name during loading.
1. formatting and emphasis mine
I am new to macros. I am trying to write a macro to generate some functions:
(defmacro test (name)
`(defun ,(intern (concat "fun-" (symbol-name name))) ()
...))
I want to pass a symbol to this macro like (test 'stuff), but emacs gives me this error:
Debugger entered--Lisp error: (wrong-type-argument symbolp (quote stuff))
Is that telling me (quote staff) is not the right argument to symbol-name ? How can I fix this ?
This all seems OK. The only thing is that you should call your macro with an unquoted symbol, like this:
(test stuff)
The reason for this is that, as noted in the Emacs Lisp Manual :
Macros [...] operate on the unevaluated expressions for the arguments, not on the argument values as functions do.
When you pass symbols to functions, you are used to quote them to prevent them being considered as a variable name and evaluated to the associated value. However, macro arguments are not evaluated during the macro expansion, but afterwards, when the expansion itself gets evaluated.