Skip to content

LISP - Using mapc on List

Lisp - Iterating for Side Effects with mapc

Section titled “Lisp - Iterating for Side Effects with mapc”

Common Lisp provides a family of powerful map functions for iterating over sequences. While mapcar is used to create a new list by applying a function to each element, mapc is used purely for its side effects. It applies a function to each element but discards the results, always returning the original list.

(mapc function list &rest more-lists)
  • function: The function to apply to each element. This can be a named function (e.g., #'print) or a lambda function.
  • list: The list(s) to iterate over.
  • Return Value: mapc returns its first list argument. The return values of the function calls are ignored.

The key difference to remember:

  • mapcar (car for ‘c’ontents): Collects results. Use it when you want to transform a list into a new one.
  • mapc (c for ‘c’onditional/side-effect): Discards results. Use it when you want to do something with each element, like printing, modifying a file, or updating another data structure.

Printing is a classic side effect. Since we don’t need to collect any results, mapc is a perfect fit.

;; Prints each number on a new line
(mapc #'print '(10 20 30))
10
20
30

Note that the REPL will also print the return value of mapc, which is the original list (10 20 30).

Here, we use mapc to iterate over a list of numbers and add them to a running total. Modifying total is the side effect. Note the *earmuffs* convention for special (global) variables.

(defvar *total* 0 "A running total, for demonstration.")
(defun add-to-total (numbers)
(mapc (lambda (x) (incf *total* x))
numbers)
;; mapc returns the original list, but we are interested in the final total.
*total*)
;; Reset total and run the function
(setf *total* 0)
(add-to-total '(1 2 3 4 5))
15

Just like mapcar, mapc can operate on multiple lists simultaneously. The provided function must accept a number of arguments equal to the number of lists. Iteration stops as soon as the shortest list is exhausted.

(let ((users '("Alice" "Bob" "Charlie"))
(ids '(101 102 103)))
(mapc (lambda (user-name user-id)
(format t "Processing user: ~a (ID: ~a)~%" user-name user-id))
users ids))
Processing user: Alice (ID: 101)
Processing user: Bob (ID: 102)
Processing user: Charlie (ID: 103)

For simple loops with side effects, the dolist macro is often more readable and is a very common alternative to mapc.

;; Using mapc
(mapc #'print '(a b c))
;; Using dolist - often considered more direct and clearer
(dolist (item '(a b c))
(print item))

Choose dolist for simple, single-list iteration. Choose mapc when you want a more functional style or need to iterate over multiple lists in parallel.