Skip to content

LISP - Functions

Functions are the fundamental building blocks of any Lisp program. They are self-contained units of code that perform a specific task. In Lisp, functions are first-class citizens, meaning they can be stored in variables, passed as arguments to other functions, and returned as values, just like any other data type like numbers or strings.

The defun macro is used to define a named function. Its basic syntax is:

(defun function-name (parameter-list)
"Optional documentation string."
function-body)
  • function-name: A symbol that names the function.
  • parameter-list: A list of symbols for the function’s arguments.
  • documentation string: An optional string describing what the function does. This is a best practice and can be retrieved with (documentation 'function-name 'function).
  • function-body: One or more Lisp expressions. The value of the last expression evaluated is implicitly returned as the function’s result.

Let’s define a function to calculate the area of a circle. Note the use of the built-in constant pi.

(defun circle-area (radius)
"Calculates the area of a circle given its radius."
(* pi radius radius))
;; Call the function and print the result
(format t "Area of a circle with radius 10: ~f~%" (circle-area 10))
Area of a circle with radius 10: 314.15927

Lisp functions support highly flexible parameter lists using lambda list keywords.

  • &optional: For optional arguments that can have default values.
  • &rest: For variadic functions that can accept any number of additional arguments.
  • &key: For arguments that are specified by name, not position.
(defun greet (name &key (greeting "Hello") (punctuation "!"))
"Greets a person with customizable options."
(format nil "~a, ~a~a" greeting name punctuation))
;; --- Usage ---
;; Default arguments
(print (greet "Alice"))
;; Override one keyword argument
(print (greet "Bob" :greeting "Hi"))
;; Override all keyword arguments (order doesn't matter)
(print (greet "Charlie" :punctuation "." :greeting "Good morning"))
"Hello, Alice!"
"Hi, Bob!"
"Good morning, Charlie."

As mentioned, a function implicitly returns the value of its last evaluated expression. For early returns from inside a block (like a loop), you can use return-from.

(defun find-first-odd (numbers)
"Returns the first odd number in a list, or NIL if none are found."
(dolist (num numbers) ; dolist implicitly creates a block named nil
(when (oddp num)
(return-from find-first-odd num)))
;; This part is only reached if the loop completes without finding an odd number.
nil)
(print (find-first-odd '(2 4 5 6 7)))
(print (find-first-odd '(2 4 6 8)))
5
NIL

The defun macro automatically wraps the function body in a block with the same name as the function. (return-from find-first-odd num) immediately exits the find-first-odd function and returns the value of num.

A higher-order function is a function that either takes another function as an argument or returns a function. This is a cornerstone of functional programming.

Anonymous (unnamed) functions can be created on the fly using the lambda special form.

mapcar is a classic higher-order function. It applies a given function to each element of a list and returns a new list with the results.

;; Define a list of numbers
(defparameter *numbers* '(1 2 3 4 5))
;; Use mapcar with a named function (sqrt)
(print (mapcar #'sqrt *numbers*))
;; Use mapcar with an anonymous lambda function to square each number
(print (mapcar (lambda (x) (* x x)) *numbers*))
(1.0 1.4142135 1.7320508 2.0 2.236068)
(1 4 9 16 25)
  • #'sqrt: The #' (sharp-quote) syntax is shorthand for (function sqrt). It retrieves the function object associated with the symbol sqrt.
  • (lambda (x) (* x x)): Creates a function that takes one argument x and returns its square. This function object is passed directly to mapcar.