Lisp Troubles: Fabricating a Closure… October 7th, 2009
Patrick Stein

Edit: lispnik on Livejournal pointed out this section of the Hyperspec which gaurantees that my pointer math is valid. I’d still like to know how to make a closure like this though, for future reference.

Edit(2): See tfb’s comment below for a solution that does make a closure using a nested (LAMBDA …) inside the backquote.

My Fast Fourier Transform library operates on multi-dimensional arrays. When you do a Fourier Transform on a two-dimensional array, you do a Fourier Transform on each row and then on each column of the result (or each column and then on each row of the result). When you do a three-dimensional Fourier Transform, you do a two-dimensional Fourier Transform on each horizontal slice and then a one-dimensional Fourier Transform on each vertical column.

The problem

To accomplish this on arrays of any number of dimensions, I wrote a wrapper class called virtual-row that one can use to index any axis-parallel row in a data hypercube with an API that makes it look like a one-dimensional array. So, for example, if I had a four-dimensional array, zero-ing one of those rows explicitly might look like this:

(dotimes (kk (array-dimension array 2))
    (setf (aref array 1 3 kk 5) 0)

In my abstracted world, it would look like this:

(let ((row (virtual-row array '(1 3) '(5))))
  (dotimes (kk (row-length row))
    (setf (row-ref row kk) 0)))

A simple, straightforward implementation of the virtual row setter function might look like this:

(defun (setf row-ref) (row index value)
  (with-slots (array pre post) row
    (let ((index (apply #'array-row-major-index
                        array
                        (append pre (list index) post))))
      (setf (row-major-aref index) value))))

The problem with that implementation is that it allocates a new list every time. This function is called O(n \log n) times per row (and there are \sum_{i=1}^{k} \prod_{i\neq j} d_j virtual rows in a k-dimensional hypercuboid where the j-th dimension has d_j entries).

The Goal

What I wanted to do instead was to generate a closure that incorporated the pre and post lists. What I wanted was almost this:

(defun generate-setter (array pre post)
  (eval `(lambda (index value)
           (setf (aref array ,@pre index ,@post) value))))

I say almost because (EVAL …) occurs in the null lexical environment. This means that it doesn’t capture the array variable. If I put a comma in front of the array, then I get a representation of the value to which array is bound, which doesn’t help me. Nor does passing in ’array since array is a lexical variable where I’m calling it from, too.

Putting the (EVAL …) inside the (LAMBDA …) doesn’t help because then it happens at runtime and I just spend all of my time in crazy functions like (APPEND …) and (READ-FROM-STRING …).

I tried assembling a list by hand instead of with the backquote. This was hampered both by the fact that (LAMBDA …) is a macro and because I still couldn’t capture a lexical variable (only its value). Other attempts that lead nowhere are:

(let ((aa "ABCDEFG"))
  (let ((ff (read-from-string "(lambda () aa)")))
    (funcall ff)))

(let ((aa "ABCDEFG"))
  (let ((ff (compile nil (macroexpand '(lambda () aa)))))
    (funcall ff)))

The (READ-FROM-STRING …) tells me that AA is UNBOUND. This is unsurprising. Why should it care about my current lexical environment?

The (MACROEXPAND …) gives me a fabulous message under SBCL:

FUNCTION fell through ECASE expression.                                        
Wanted one of (SB-C:LAMBDA-WITH-LEXENV LAMBDA                                  
               SB-KERNEL:INSTANCE-LAMBDA SB-INT:NAMED-LAMBDA).
   [Condition of type SB-KERNEL:CASE-FAILURE]

It seems like it might be slightly possible to work something out with an extra macro-level and some use of the &ENVIRONMENT lambda list keyword. But, I am not even sure where to start trying to figure out how that all plays together.

The Hack

Having failed to make a good closure on the fly and not being interested in consing as much as the simple implementation above does, I have settled instead for cheating. Here is how I am cheating.

(defun generate-setter (array pre post)
  (flet ((compute-index (pre index post)
           (apply #'array-row-major-index
                  (append pre (list index) post))))
    (let* ((axis   (length pre))
           (offset (compute-index pre 0 post))
           (second (min 1 (1- (array-dimension array axis))))
           (span   (- (compute-index pre second post) offset)))
      (lambda (index value)
        (setf (row-major-aref array (+ (* index span) offset)) value)))))

The idea here is to compute the difference in (ARRAY-ROW-MAJOR-INDEX …) between the first and second elements in the row. (Note: the whole business with (MIN …) is to avoid trouble if there is no second element in the row.) Then, I assume that the n-th element of the row will be n times that difference beyond the initial element of the row.

This works well and quickly. But, it is somewhat cheesy. There is no requirement that my index math be correct for any given implementation. It would shock me to find that some implementation disagreed with the above index math. Still, I’d really like to just fabricate a proper closure around (AREF …) instead.

Can anyone lend me a half a cup of clue?

9 Responses to “Lisp Troubles: Fabricating a Closure…”

  1. pat
    2009-10-08 @ 1:57 AM

    Feh, I suppose that I can get away with it if I pass in the buffer each time. I’d rather have a closure, but this will work at the expense of having to pull the buffer out of my virtual-row each call:

    (defun generate-setter (pre post)
      (eval `(lambda (buffer index value)
               (setf (aref buffer ,@pre index ,@post) value))))
  2. 2009-10-08 @ 3:54 AM

    Hi,
    not answering your question, really, but have you looked at bordeaux-fft?

    • pat
      2009-10-08 @ 7:31 AM

      No… I haven’t looked at Bordeaux FFT. It didn’t come up when I searched for FFT on the Cliki or when I searched for FFT and Lisp together on Google. I will look at it.

      Actually, it’s on my first page of Google results at the moment. I don’t know what I had searched for before, or how I had missed it. Definitely, it could use a mention on the Cliki.

  3. Zach
    2009-10-08 @ 7:21 AM

    The simple, straightforward implementation could look like this due to how APPLY and AREF are specified to work together:

    (defun (setf row-ref) (value row index)
      (with-slots (array pre post) row
        (setf (apply #'aref array (append pre (list index) post)) value)))

    (Why isn’t this post tagged “lisp”?)

    • pat
      2009-10-08 @ 7:52 AM

      I thought that the (APPLY …) would totally mess with (SETF …)‘s head. Though, I suppose that I didn’t try it that way. That was my first implementation of the non-SETF version of this function. I see now where there is an example of using (APPLY #’AREF …) in a (SETF …) in the Hyperspec. Thanks for pointing that out.

      If I can just get the closure part to work then, I’ll have a much more readable version than all this messing with ARRAY-ROW-MAJOR-INDEX.

      (As for the tag: It wasn’t tagged Lisp because I’m still lame with WordPress. I typed in “lisp” into the tags box. It showed me the list of auto-completes, and I thought I was done. I forgot that I have to commit the tags with an “enter” or a mouse click… having the tags there when I hit “Publish” isn’t enough. Oops.)

  4. tfb
    2009-10-08 @ 8:48 AM

    You were close with

    (defun generate-setter (array pre post)
      (eval `(lambda (index value)
               (setf (aref array ,@pre index ,@post) value))))

    If you generate a function that, when called, returns the closure, you can get what you were trying for there.

    (defun generate-setter (array pre post)
      (let ((make-closure
             (eval `(lambda (array)
                      (lambda (index value)
                        (setf (aref array ,@pre index ,@post) value))))))
        (funcall make-closure array)))

    And of course, you probably want to replace EVAL with COMPILE…

    • pat
      2009-10-08 @ 9:11 AM

      That… Is… Crafty….

      Very nice. Thank you.

  5. raito
    2009-10-08 @ 10:55 AM

    I had a similar problem some time ago, where EVAL took place in a null lexical environment, but needed to use some stuff defined in the function that contained the EVAL. My solution was to declare the stuff I needed inside the EVAl as special, giving it dynamic scope. Because the EVAl took place in the dynamic scope that included the special variables, I was able to access them from inside the EVAL. It looed something like this:

    ;not all the code, but you get the picture.
    (defun name (arg1 arg2)
         (declare (special arg1 arg2))
    ...
         ; the code in (nth item results) can access arg1 and arg2
         (eval (nth item results)))
    • pat
      2009-10-08 @ 11:06 AM

      I thought about that for a bit here, too. In this case though, I would then need to be sure that special was appropriately bound during each invocation of my lambda.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <br> <cite> <code> <dd> <del datetime=""> <dl> <dt> <em> <i> <img alt="" height="" longdesc="" src="" width=""> <ins datetime="" cite=""> <li> <ol> <p> <q cite=""> <s> <strike> <strong> <sub> <sup> <u> <ul>

l