3 Code is data

(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:

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