Patrick (patrickwonders) wrote in lisp,

Lisp Troubles: Fabricating a Closure...

The short version of my problem is that I want to do this:

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

Except that I want the (lambda ...) to be a closure around buffer. Here, pre and post are lists generated at runtime (thus not available as lists at compile time) and hence the (defun ...) and (eval ...) instead of (defmacro ...). Alas, the (eval ...) form uses the null lexical environment, so I cannot capture buffer.

The long form with some of the myriad failed attempts I have made is on this blog post. Please, anyone have half a cup of clue that I could borrow? I promise, I'm going to the store for more clues really soon. I'll pay you back.

Edit: Here is exactly what I was looking for... (courtesy of tfb)

(defun generate-setter (buffer pre post)
  (let ((make-closure
           (compile nil `(lambda (buf)
                           (lambda (index value)
                             (setf (aref buf ,@pre index ,@post) value))))))
    (compile nil (funcall make-closure buffer))))
  • Post a new comment


    default userpic

    Your IP address will be recorded 

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

But these EVALs are worse than allocated list in one of your attempts (that can be potentially optimized by some implementations).

You have to write your own implementation of array-row-major-index-like function to use with row-major-aref, so that closure will calculate row-major index like index*const1+const2 where const1 and const2 depend on pre and post.


October 8 2009, 12:35:19 UTC 7 years ago Edited:  October 8 2009, 12:38:40 UTC

But, the Eval only gets invoked once per row... not every time I invoke the setter. I don't call it like (funcall (generate-setter buffer pre post) index value). I store the output of (generate-setter buffer pre post) in my virtual-row struct and use it for all O(n lg n) row accesses.

And, yes, I did write my own implementation that did the (index*const1 + const2), but it feels like cheating to assume that if (apply #'array-row-major-index buffer (append pre (list 0) post)) is x and (apply #'array-row-major-index buffer (append pre (list 1) post)) is y, that (apply #'array-row-major-index buffer (append pre (list ii) post)) is going to be (x-y)*ii + x. That isn't guaranteed by the Hyperspec, as near as I can tell. [Though it would be an incredibly odd implementation that did otherwise.]
Ah... Thank you. I just read the array-row-major-index and row-major-aref pages.
why reinvent the wheel (reimplement row-major access)?
just use (setf apply).
It looks like you want a macro. In that case, just replace defun with defmacro.
And remove eval, of course...
No, because pre and post are not set at compile time.

It turns out that making one lambda in there works. See the comments at the blog post.
(defun generate-setter (buffer pre post)
(lambda (index value)
(setf (apply #'aref buffer (append pre (list index) post)) value)))
Yes, but that does an append every time I invoke the setter. I call in n lg n times with the same pre and post.
then you should close over a pre-existing list:
(defun foo (buf pre post)
(let ((index-tail (cond nil post))
(index-all (append pre index-tail)))
(lambda (index value)
(setf (car index-tail) index)
(setf (apply #'aref buf index-all) value))))
But, then I don't trust using it with rotatef and setf and threading....
what is "it" you don't trust?
That two different threads of execution will both modify the same list at the same time... and one might get what the other wanted....
well, then just use append and do not reuse the list.
or maybe use compile instead of eval in your original code.
btw, your new version calls compile twice for no good reason.


October 8 2009, 22:55:52 UTC 7 years ago Edited:  October 8 2009, 22:58:45 UTC

The second compile is because I really want the function being returned to be compiled. The first, I had to either eval or compile since I cannot funcall a literal list whose first element is lambda... That lambda has never been interpreted.

Also, since I plan to call this a few more than two thousand times, I don't want it allocating memory on every invocation if it doesn't have to.
note that you original "eval" version does the same.
i.e., it conses _more_ than my code because on top of the list of indexes it conses up the lambda and produces and interpreted closure.
note that both my versions produce compiled closures (if, of course, the function itself is compiled).
The goal in my code was to make it so that I can create a setter for this virtual "row" of the matrix and then use it a bunch of times in a row. With my eval case (change it to (compile nil ...) if you like for your implementation) was to only cons the list together once... get it as a function... and call it a bunch of times... those calls do not have to cons.

Your code invoked append every time I called the function it produced. My code only invoked append while producing the function.