Defining and acessing variables in a local environment

Viewed 44

After quite a while of studying CL and practising as a hobby in different small projects, I still have some blank areas on my personal CL map. Recently, I had a number of functions using all the same let construct and I thought of writing a macro which makes the code more concise:

(defmacro with-context (&body body)
   `(let ((context (make-array 4 :element-type 'fixnum
                                 :initial-contents '(0 1 2 3))))
     ,@body))

So that I can later define functions like (just as a minimal example):

(defun test-a ()
  (with-context
    (setf (aref context 3)
          (+ (aref context 0) (aref context 1)))
    context))

Now I was wondering if I could shorten the (aref context n) expressions with a macro/function like (context n).

(defun context (n)
  (aref context n))

But the variable context is unknown at compile time, of course. I just don't know if I have a case of basic misunderstanding here or how I could tell lisp what I actually want. So, my question is basically if it is possible and if it is a good idea.

2 Answers

What's wrong with a local function?

(defmacro with-context (&body body)
  `(let ((context (make-array 4 :initial-contents '(0 1 2 3))))
     (flet ((context (n)
              (aref context n)))
       ,@body)))

Setting, too:

(defmacro with-context (&body body)
  `(let ((context (make-array 4 :initial-contents '(0 1 2 3))))
     (flet ((context (n)
              (aref context n))
            ((setf context) (new n)
              (setf (aref context n) new)))
       ,@body)))

You could put a macrolet into your macro expansion:

(defmacro with-context (&body body)
  (with-gensyms (i)
    `(let ((context (make-array 4 …)))
       (macrolet ((context (,i)
                    `(aref context ,,i)))
         ,@body))))

Some personal notes:

I dislike anaphoric macros, so I'd generally let the user define the name for the context. That would feel strange with the context macrolet though. I'd then maybe come to the conclusion that all I want is a make-standard-context function and keep using let. I think this is in line with the general guideline “be conventional”.

Related