Here’s how to remove variables and functions from an image:

(makunbound 'variable)

(fmakunbound 'function)

So, for example:

(defvar foo 42)
(defun bar () (print "Hello world!"))

(boundp 'foo)                           ;; T
(fboundp 'bar)                          ;; T

(makunbound 'foo)
(fmakunbound 'bar)

(boundp 'foo)                           ;; nil
(fboundp 'bar)                          ;; nil

Read on to find out why I prefer the lisp approach over most other languages.

What does this look like in other languages?

In interpreted languages, like python and R there is the concept of removing or deleting a variable or function.

In python you del variable, in R you rm(variable):

foo = 5
foo

Naturally results in

5

Now we delete the variable:

del foo    # Delete foo
foo
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# NameError: name 'foo' is not defined

Why does common lisp appear to make it more complicated?

Why do we use makunbound and fmakunbound, etc., in common lisp?

(The following is lightly rewritten and sourced from Peter Norvig’s Paradigms of Artificial Intelligence Programming)

…because common Lisp has at least seven name spaces. The two we think of most often are

  1. Functions and Macros
  2. Variables

Python, R, Scheme, etc. conflate these two name spaces, but Common Lisp keeps them separate, so that in a function application like (f) the function/macro name space is consulted for the value of f, but in (+ f), f is treated as a variable name.

  1. Special variables form a distinct name space from lexical variables
    So the f in (+ f) is treated as either a special or lexical variable, depending on if there is an applicable special declaration.
  2. Data types
    Even if f is defined as a function and/or a variable, it can also be defined as a data type with defstruct, deftype, or defclass.
  3. Labels for go statements within a tagbody
  4. Block names for return-from statements within a block
  5. Symbols inside a quoted expression are treated as constants, and thus form name space
    These symbols are often used as keys in user-defined tables, and in a sense each such table defines a new name space. One example is the tag name space, used by catch and throw. Another is the package name space.

Just because you can do something, doesn’t mean you should

It is a good idea to limit each symbol to only one name space. Common Lisp will not be confused if a symbol is used in multiple ways, but the poor human reader probably will be.

🤣

Why do I like the lisp approach?

It cuts down on errors.

When collaborating it is easy to clash in your namespaces. You might define the variable two_pi, I might want to define two_pi as a function. My code runs, I merge it. Your code runs, you merge it. We didn’t have appropriate testing… 💣💥

Here’s a contrived example, presented in three different programming languages: R, python and (common) lisp.

Here I define a variable two_pi to be twice the value of \( \pi \), then I define a function to calculate the circumference of a circle and then I define a function (also) called two_pi. Let’s see what happens…

In R

two_pi <- 2 * pi

circumference <- function(r) {
  cat("R: The circumference is ", (two_pi * r), "\n")
}

# We all know pi is actually 3
two_pi <- function() {
  return(6);
}

circumference(1)

In this case R redefines what is meant by two_pi and the circumference function fails. Why? Because you need to call functions: two_pi()

Error in two_pi * r : non-numeric argument to binary operator

In python

import math

two_pi = 2 * pi

def circumference(r):
    print(f"python: The circumference is {two_pi * r}")

# We all know pi is actually 3
def two_pi():
    return 6

circumference(1)

Python also redefines what is meant by two_pi and the circumference function fails.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in circumference
TypeError: unsupported operand type(s) for *: 'function' and 'int'

In lisp

(setf two_pi (* 2 pi))

(defun circumference (r)
  (format t "cl: The circumference is ~a~&" (* two_pi r)))

;; We all know pi is actually only 3.
(defun two_pi ()
  6)

(circumference 1)

Common lisp works, because it keeps functions and variables in difference namespaces and knows which one to call at which point.

cl: The circumference is 6.283185307179586d0