Skip to content

Defining New Macros in LISP

Vanessa Hassouna edited this page Aug 10, 2023 · 1 revision

Defining New Macros in LISP

Recommendation:
Before defining new macros, consider if they are truly necessary. While macros can make code more readable, they can also complicate debugging.

Basic Structure of a Macro:
In LISP, a macro can be defined using the following structure:

(defmacro macro-name (parameters) 
   "Documentation"
   macro-body)

What are Macros?
Macros allow you to create custom syntax in LISP. They work by transforming code and introducing new notations. Unlike functions, the body of a macro is computed during compilation. This means you can use macros to generate code that gets expanded at compile-time.

Examples:

  1. A simple macro:
(defmacro forty-two () (* 2 21))

Using (forty-two) is the same as directly writing 42.

  1. A macro with quoted LISP expressions:
(defmacro forty-two () '(* 2 21))

Here, (forty-two) is equivalent to (* 2 21), which evaluates to 42 during runtime.

Parameters in Macros

Macros accept parameters like functions. You can use &optional, &key, and &rest. Additionally, macros offer &body, which is like &rest but more descriptive, aiding both human readers and development tools.

Using Backquote and Comma

For ease of writing and reading complex expressions, LISP macros use backquotes (`) and commas (,). The backquote marks the start of an expression. Within that expression, a comma indicates subexpressions to be evaluated. Others are left quoted.

Consider the measure-time macro:

(defmacro measure-time (type &body body)
    "Description"
    `(let ((start ...)
          (value ...))
       ,...
       value))

In this macro, , tells LISP which parts to evaluate and which to leave quoted.

Splicing

Splicing with ,@ lets you remove the outermost parentheses. It's useful when you want the code inside a block like (progn ...). The measure-time macro showcases this.

Example Macro: with-simulated-robot

This macro simulates a robot's actions:

(defmacro with-simulated-robot (&body body)
    `(let ((results
            (proj:... 
              (cpl-impl:...
                ,@body))))
       (car ...)))

When you use:

(with-simulated-robot
    some code)

The "body" contains "some code". The macro evaluates as much of the body as possible during macro expansion. Then, it passes results to other functions/macros that run during runtime. Given the name "with-simulated-robot", it's clear that the code should simulate a robot's actions in real-time, not during macro expansion.


In essence, LISP macros allow for powerful code transformations. They enable you to extend the language's syntax, but it's essential to use them judiciously to maintain code clarity and ease of debugging.