From: Sacha
Subject: 2 macros and passing gensyms around
Date: 
Message-ID: <d7T4h.176357$6l3.2752585@phobos.telenet-ops.be>
Hello all,

I have a question that will require quite a bit of an explanation. I hope 
this won't be too long =P

I have this table object that holds column objects, and a collection of 
records.
Each record is an array of values.

I need to access specific fields of these records.

So here goes the standard code :
(defun field-value (record column-name)
    (aref (data record) (get-column-index (table record) column-name)))

(field-value record 'day) ;retreives the value of the day field on this 
record

This is fine, but i have lots of records and need to make it as fast as 
possible.
It seems that i can achieve double performances by doing a direct array 
access, bypassing the column lookup.

So i made these two macros :

(defmacro with-bindable-columns ((&rest var-and-column-names) table-or-index 
&body body)
  (let ((table (gensym "TABLE-OR-INDEX")))
    `(let* ((,table ,table-or-index)
            ,@(loop for var-and-column-name in var-and-column-names
                    when (listp var-and-column-name)
                    collect (list (conc-symbol (first var-and-column-name) 
"-idx")
                                  `(get-column-index ,table ',(second 
var-and-column-name)))
                    when (symbolp var-and-column-name)
                    collect (list (conc-symbol var-and-column-name "-idx")
                                  `(get-column-index ,table 
',var-and-column-name))))
       ,@body)))

(defmacro bind-vars ((&rest vars) data &body body)
  `(let ,(loop for var in vars
               collecting `(,var (aref ,data ,(conc-symbol var "-idx"))))
     ,@body))

Here is a sample usage :

(defun get-first-date ()
  (let ((lowest nil))
    (with-bindable-columns (day)
        *stats-table*
      (do-table-records (*stats-table* record)
        (bind-vars (day)
            (data record)
          (when (or (null lowest)
                    (< day lowest))
            (setf lowest day)))))
    lowest))

My problem here is the name to give to those -idx variables.
In order to avoid variable capture, i would like to give these a GENSYM 
name.
The name would be created in the with-bindable-columns macro and used in the 
bin-vars macro.
And of course, i'm clueless as how to do this.
Would it be possible to tell the compiler sometyhing like : "hey ! this 
_compiler_ variable holds some data that will only be available within the 
lexical scope of that macro expansion" ?

Thanks in advance,
Sacha 

From: Pascal Bourguignon
Subject: Re: 2 macros and passing gensyms around
Date: 
Message-ID: <87fycrr6eg.fsf@thalassa.informatimago.com>
"Sacha" <··@address.spam> writes:

> (defmacro with-bindable-columns ((&rest var-and-column-names) table-or-index 
> &body body)
>   (let ((table (gensym "TABLE-OR-INDEX")))
>     `(let* ((,table ,table-or-index)
>             ,@(loop for var-and-column-name in var-and-column-names
>                     when (listp var-and-column-name)
>                     collect (list (conc-symbol (first var-and-column-name) 
> "-idx")
>                                   `(get-column-index ,table ',(second 
> var-and-column-name)))
>                     when (symbolp var-and-column-name)
>                     collect (list (conc-symbol var-and-column-name "-idx")
>                                   `(get-column-index ,table 
> ',var-and-column-name))))
>        ,@body)))
>
> (defmacro bind-vars ((&rest vars) data &body body)
>   `(let ,(loop for var in vars
>                collecting `(,var (aref ,data ,(conc-symbol var "-idx"))))
>      ,@body))
>
> Here is a sample usage :
>
> (defun get-first-date ()
>   (let ((lowest nil))
>     (with-bindable-columns (day)
>         *stats-table*
>       (do-table-records (*stats-table* record)
>         (bind-vars (day)
>             (data record)
>           (when (or (null lowest)
>                     (< day lowest))
>             (setf lowest day)))))
>     lowest))
>
> My problem here is the name to give to those -idx variables.
> In order to avoid variable capture, i would like to give these a GENSYM 
> name.
> The name would be created in the with-bindable-columns macro and used in the 
> bin-vars macro.
> And of course, i'm clueless as how to do this.
> Would it be possible to tell the compiler sometyhing like : "hey ! this 
> _compiler_ variable holds some data that will only be available within the 
> lexical scope of that macro expansion" ?

MACROLET


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Pour moi, la grande question n'a jamais �t�: �Qui suis-je? O� vais-je?� 
comme l'a formul� si adroitement notre ami Pascal, mais plut�t: 
�Comment vais-je m'en tirer?� -- Jean Yanne
From: Sacha
Subject: Re: 2 macros and passing gensyms around
Date: 
Message-ID: <SK35h.177380$744.2883613@phobos.telenet-ops.be>
>> Here is a sample usage :
>>
>> (defun get-first-date ()
>>   (let ((lowest nil))
>>     (with-bindable-columns (day)
>>         *stats-table*
>>       (do-table-records (*stats-table* record)
>>         (bind-vars (day)
>>             (data record)
>>           (when (or (null lowest)
>>                     (< day lowest))
>>             (setf lowest day)))))
>>     lowest))
>>
>> My problem here is the name to give to those -idx variables.
>> In order to avoid variable capture, i would like to give these a GENSYM
>> name.
>> The name would be created in the with-bindable-columns macro and used in 
>> the
>> bin-vars macro.
>> And of course, i'm clueless as how to do this.
>> Would it be possible to tell the compiler sometyhing like : "hey ! this
>> _compiler_ variable holds some data that will only be available within 
>> the
>> lexical scope of that macro expansion" ?
>
> MACROLET
> -- 
> __Pascal Bourguignon__                     http://www.informatimago.com/

Here is the modified version of this macro :

(defmacro with-bindable-columns ((&rest var-and-column-names) table-or-index 
&body body)
  (let ((table (gensym "TABLE-OR-INDEX"))
        (var-indexes nil))
    (flet ((gen-idx-var (var-name)
             (let ((result (gensym (concatenate 'string (symbol-name 
var-name) "-IDX"))))
               (push result var-indexes)
               (push var-name var-indexes)
               result)))
    `(let* ((,table ,table-or-index)
            ,@(loop for var-and-column-name in var-and-column-names
                    if (listp var-and-column-name)
                    collect (list (gen-idx-var (first var-and-column-name))
                                  `(get-column-index ,table ',(second 
var-and-column-name)))
                    else if (symbolp var-and-column-name)
                    collect (list  (gen-idx-var var-and-column-name)
                                  `(get-column-index ,table 
',var-and-column-name))))
       (macrolet ((bind-vars ((&rest vars) data &body body)
                    `(let ,(loop for var in vars
                                 collecting `(,var (aref ,data ,(getf 
',var-indexes var))))
                       ,@body)))
         ,@body)))))

Pretty hairy considering the double backquote syntax.
Thanks a lot Pascal.

Sacha