(Date: 5 Jan 2024)
Summary: Code is data, and can be manipulated and evaluated. This is very direct in Lisp. I go through examples in Emacs Lisp (elisp).
The concept of ‘code is data, data is code’ is the case in Lisp. (It is
also true for assembly language). In Lisp you can manipulate s-exps
representing data or function calls. You can call the result in two
ways. The special form funcall
is for calling functions that are
only known at runtime. apply
is for the arguments are also known at
runtime.
From the elisp manual:
When you write a list as an expression in your program, you specify which function to call, and how many arguments to give it, in the text of the program. Usually that’s just what you want. Occasionally you need to compute at run time which function to call. To do that, use the function ‘funcall’. When you also need to determine at run time how many arguments to pass, use ‘apply’.
eval
:
Given a sexp, for example that you construct on-the-fly, you can call
the eval
special form on it, to send it to the Lisp evaluator. The
evaluator is a fundamental part of Lisp. It is written in C (see
eval.c). Example:
(setq my-sexp '(message "hello")) (eval my-sexp)
apply
:
The elisp manual further suggests the example of mapcar
as an application of
apply
, as below. (Note however the current implementation of
mapcar
, in Emacs 29.1 is in C)
(defun mapcar* (function &rest args) "Apply FUNCTION to successive cars of all ARGS. Return the list of results." ;; If no list is exhausted, (if (not (memq nil args)) ;; apply function to CARs. (cons (apply function (mapcar #'car args)) (apply #'mapcar* function ;; Recurse for rest of elements. (mapcar #'cdr args)))))
Examples in Emacs source of funcall
and apply
:
funcall
is for calling a function with all its arguments.
apply
is for calling a function with a single list argument.
This is the argument list (arglist) of the function to call. We say
apply
‘spreads’ the list of arguments, converting it into each
argument.
Emacs has the convention of hooks which are defined entry points for
extension. As expected run-mode-hooks
uses funcall
(See
subr.el). (Although run-hooks
is implemented in C.)
(defun run-mode-hooks (&rest hooks) ... (dolist (fun (prog1 (nreverse delayed-after-hook-functions) (setq delayed-after-hook-functions nil))) (funcall fun)))
Other examples of funcall
and apply
follow in browse-url.el.
The variables `browse-url-browser-function', `browse-url-handlers', and `browse-url-default-handlers' determine which browser function to use.
This includes for example eww-browse-url
or browse-url-firefox
.
browse-url
, uses apply
:
(defun browse-url (url &rest args) ... (let ((process-environment (copy-sequence process-environment)) (function (or (browse-url-select-handler url) browse-url-browser-function))) ... (if (functionp function) (apply function url args) (error "No suitable browser for URL %s" url))
browse-url--browser
, uses funcall
:
(defun browse-url--browser (url &rest args) "Call `browse-url-browser-function' with URL and ARGS." (funcall browse-url-browser-function url args))