Skip to content

LISP - Calling Function using apply

Lisp - Invoking Functions Dynamically with apply and funcall

Section titled “Lisp - Invoking Functions Dynamically with apply and funcall”

Lisp is renowned for its homoiconicity (code is data), which allows for powerful metaprogramming. A key part of this is the ability to call functions dynamically. The apply and funcall functions are the primary tools for this job. They allow you to invoke a function that is determined at runtime.

Both funcall and apply call a function, but they differ in how they accept arguments:

  • funcall: Takes a function object followed by its arguments, one by one.
  • apply: Takes a function object, any number of initial arguments, and a list containing the remaining arguments.
;; The goal is to compute (+ 10 20 30)
;; Using FUNCALL: Arguments are passed individually.
(funcall #'+ 10 20 30)
;=> 60
;; Using APPLY: Arguments are passed in a list.
(apply #'+ '(10 20 30))
;=> 60
;; APPLY can also take initial arguments before the list.
(apply #'+ 10 '(20 30))
;=> 60
;; A common mistake is to give funcall a list.
; (funcall #'+ '(10 20 30)) ; This would be an ERROR!

The key takeaway: Use apply when your arguments have already been collected into a list.

Imagine you need a function that takes an operation (like + or *) and a list of numbers, and returns the result. This is a perfect scenario for apply, because the arguments for the operation are already in a list.

(defun calculate (operation numbers)
"Applies a given operation to a list of numbers."
(unless (listp numbers)
(error "'numbers' must be a list."))
(apply operation numbers))
;; --- Using the calculate function ---
;; Calling with the symbol for the addition function
(print (calculate '+ '(10 20 30)))
;; Calling with a lambda function
(print (calculate (lambda (x y) (- x y)) '(100 25)))
;; Calling with a predefined function (works for any arity)
(print (calculate #'max '(1 8 5 9 2)))

When you execute the code, it returns the following result:

60
75
9

apply is essential for writing wrapper functions or macros that need to pass their arguments along to another function. This is often done in combination with &rest parameters in a function definition.

(defun logging-wrapper (function-to-call &rest args)
"A wrapper that logs before and after calling a function with its arguments."
(format t "--> Calling ~s with arguments ~s~%" function-to-call args)
;; 'args' is a list, so we must use APPLY to call the function.
(let ((result (apply function-to-call args)))
(format t "<-- Function ~s returned ~s~%" function-to-call result)
result))
;; Using the wrapper
(logging-wrapper #'+ 1 2 3)
--> Calling #<FUNCTION +> with arguments (1 2 3)
<-- Function #<FUNCTION +> returned 6
  • #' Notation: Using #' (sharp-quote) is the modern, preferred way to get a function object (e.g., #'+). It’s more explicit than using a bare symbol like '+.
  • Dynamic Dispatch: apply and funcall are the heart of dynamic dispatch in Lisp. They allow you to write flexible, generic code that decides which functions to call based on runtime conditions.
  • Error Handling: Be mindful that calling apply or funcall with the wrong number or type of arguments for the target function will result in a runtime error, just like a direct call would.