Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Q: local static variables?

203 views
Skip to first unread message

Antonio Leitao

unread,
Mar 16, 1996, 3:00:00 AM3/16/96
to
Hello World.

I was wondering if we can have static local variables.

To be more precise, I would like to introduce a new special form,
called 'let-static' or 'static-let' or whatever, with the exactly
same syntax as a regular 'let', but where variable are initialized
just once an keep their values forever.

As an example, we can think of the following function:

(defun test1 ()
(let-static ((y 0))
(incf y)
y))

(test1) -> 1
(test1) -> 2
(test1) -> 3
...

We know that we could create an equivalent function IF WE COULD
define the variable 'y' outside the definition of 'test1', something
such as:

(let ((y 0))
(defun test2 ()
(incf y)
y))

The problem is: this kind of expansion is well beyond 'usual' macro
expansion possibilities.

Let's try another possibility. We can use a symbol (a gensym) to store
the static environment on the first time the function is applied and
we funcall that symbol always. The macro will look like:

(defmacro let-static (vars&vals &body body)
(let ((func (gensym)))
`(let ()
(declare (special ,func))
(unless (boundp ',func)
(setq ,func
(let ,vars&vals #'(lambda () ,@body))))
(funcall ,func))))

It is important to remember that this macro explores a 'special feature':
every newly created gensym is unbound. We use this to distinguish the
'first time' from the others. We can exclude the gensym if we use some
tricks but the macro becomes more complex and it doesn't solve the
problems I want to discuss.

If we compile test1, everything is OK.

(compile 'test1) -> test1
(test1) -> 1
(test1) -> 2
(test1) -> 3
...

We are close but there are some problems.
First, if we did not compile 'test1', and the function were
interpreted, the macro would be expanded on every invocation and the
bindings would not be static. Unfortunately, I don't know of any
interpreter flag that force macro displacement (like on good old
FranzLisp). Anyway, it is a bad solution if we only manage
to make the function work when we compile it.

We can force macro displacement by 'hand' if we destructively update an
'&whole' macro parameter.

(defmacro let-static (vars&vals &body body &whole whole)
(let ((func (gensym)))
(let ((result
`(let ()
(declare (special ,func))
(unless (boundp ',func)
(setq ,func
(let ,vars&vals #'(lambda () ,@body))))
(funcall ,func))))
(setf (car whole) (car result)
(cdr whole) (cdr result))
result)))

Now, we can run the function interpreted or compiled:

(defun test1 ()
(let-static ((y 0))
(incf y)
y))
TEST1

(test1) -> 1
(test1) -> 2
(test1) -> 3
(compile 'test1) -> TEST1
(test1) -> 4
(test1) -> 5
...


It looks good, but there are still more problems:

The first one is that I don't know if this kind of '&whole' parameter
modification is portable, but that's not the main problem.

To see the second problem, let's suppose a different test:

(defun test3 (x)
#'(lambda ()
(let-static ((y x))
(incf y)
y)))
TEST3

(setf test3-10 (test3 10)) -> a closure
(funcall test3-10) -> 11
(funcall test3-10) -> 12
(funcall test3-10) -> 13
(setf test3-100 (test3 100)) -> another closure
(funcall test3-100) -> 14 ;;Error. Should be 101

The first time the function 'test3' is invoked, the macro is expanded
(and displaced) and we end up with a unique gensym when we should have
one for every invocation.

This time, we would like the macro to be expanded as many times as the
function 'test3' is invoked, but just one time for every invocation of
a test3 returned closure.

This behavior is clearly not feasible.

The general law is (I guess) that we want to create a different static
environment for every function created, be it global or local.

How can we solve all these problems?
Is it even possible?
I would be very glad if someone could discuss these matters.

By the way, we have already some other solutions, but they are even
more complex and still don't solve all the problems.

Thanks in advance.

Antonio Leitao

Erik Naggum

unread,
Mar 17, 1996, 3:00:00 AM3/17/96
to
[Antonio Leitao]

| I was wondering if we can have static local variables.

check out `closure' in your friendly Lisp textbook.

| We know that we could create an equivalent function IF WE COULD define
| the variable 'y' outside the definition of 'test1', something such as:
|
| (let ((y 0))
| (defun test2 ()
| (incf y)
| y))

precisely. have you tried this? if it fails, which Lisp are you using?

#<Erik>
--
the Internet made me do it

Barry Margolin

unread,
Mar 17, 1996, 3:00:00 AM3/17/96
to
In article <30360152...@arcana.naggum.no>,
Erik Naggum <er...@naggum.no> wrote:
>[Antonio Leitao]

>| We know that we could create an equivalent function IF WE COULD define
>| the variable 'y' outside the definition of 'test1', something such as:
>|
>| (let ((y 0))
>| (defun test2 ()
>| (incf y)
>| y))
>
>precisely. have you tried this? if it fails, which Lisp are you using?

I don't think he's saying that this doesn't work, just that it's not the
way he wants to do it. He would probably like to be able to define his
static variables in the scope where they're used, as is possible in other
languages.

The problem with the above is that there's no way to get LET-STATIC inside
the function definition to expand into something like it. And furthermore,
if there were another Y in the function, e.g.

(defun test2 ()
(let ((y ...))
...


(let-static ((y 0))
(incf y)

y)))

moving the static Y outside the function wouldn't solve the problem.
--
Barry Margolin
BBN PlaNET Corporation, Cambridge, MA
bar...@bbnplanet.com
Phone (617) 873-3126 - Fax (617) 873-6351

Mike McDonald

unread,
Mar 20, 1996, 3:00:00 AM3/20/96
to
In article <4inl2f$r...@sparky.franz.com>,
k...@math.ufl.edu (Kelly Murray) writes:
>In article <woka0lc...@sol.gia.ist.utl.pt>, a...@gia.ist.utl.pt (Antonio Leitao) writes:
>>> Hello World.

>>>
>>> I was wondering if we can have static local variables.
>>> As an example, we can think of the following function:
>>>
>>> (defun test1 ()
>>> (let-static ((y 0))
>>> (incf y)
>>> y))
>>>
>>> (test1) -> 1
>>> (test1) -> 2
>>> (test1) -> 3
>>> ...
>>>

>You can have let-static replace the static variable with a global gensym,
>whose value is initialized at the time the file is loaded.
>The let-static macro can then collect all the static references so
>they can be initialized when the file is loaded.
>
>(defun compile-static-file (infile outfile)
> (with-open-file (in infile :direction :input)
> (with-open-file (out outfile :direction :output :if-exists :supersede)
> (print `(eval-when (compile) (setf *static-vars* nil)) out) ;; init to nil
> (loop for form = (read in nil nil)
> while form do (print form out)) ;; copy input to output
> (print `(initialize-statics) out) ;; now do initializations after expansion
> )))
>
>
>(defvar *static-vars* nil)
>
>(defmacro let-static ( ((var initval)) &body body) ;; only handles one var
> (let ((new-var (gensym)))
> (push (cons new-var initval) *static-vars*)
> `(symbol-macrolet ((,var ,new-var)) ;; replace var with gensym.
> (locally (declare (special ,new-var)) ;; avoid compiler warnings
> ,@body))))
>
>(defmacro initialize-statics ()
> `(progn ,@(mapcar #'(lambda (bind) `(setq ,(car bind) ,(cdr bind))) ;; set gensym
> *static-vars*)))


How about this instead:

(defmacro let-static ( ((var initval)) &body body) ;; only handles one var
(let ((new-var (gensym)))
`(symbol-macrolet ((,var ,new-var)) ;; replace var with gensym.
(locally (declare (special ,new-var)) ;; avoid compiler warnings
(if (not (symbol-boundp ,var))
(setq ,var ,initval))
,@body))))

It gets rid of the global list and the compile file stuff.


Mike McDonald
mik...@engr.sgi.com


Kelly Murray

unread,
Mar 20, 1996, 3:00:00 AM3/20/96
to
In article <woka0lc...@sol.gia.ist.utl.pt>, a...@gia.ist.utl.pt (Antonio Leitao) writes:
>> Hello World.
>>
>> I was wondering if we can have static local variables.
>> As an example, we can think of the following function:
>>
>> (defun test1 ()
>> (let-static ((y 0))
>> (incf y)
>> y))
>>
>> (test1) -> 1
>> (test1) -> 2
>> (test1) -> 3
>> ...
>>

Basically, this isn't possible using normal Lisp evaluation rules
because the only way to establish a unique binding is to have
the macro expand only ONCE for each useage. Otherwise, there is
no way to know the context of the expansion is the same as a previous one.
In an interpreter, the macroexpansion can be done multiple times.
So then you're really asking for the impossible.
Static variables are only supported in "compiled file" languages.
If you limit yourself to the same useage, then you can do it in CommonLisp.

Mike McDonald

unread,
Mar 21, 1996, 3:00:00 AM3/21/96
to
In article <wovijyw...@sol.gia.ist.utl.pt>,
a...@gia.ist.utl.pt (Antonio Leitao) writes:

>The 'case' macro receive any number of clauses and although the order
>of the clauses is unimportant, they are checked in sequence until one
>of them matches the key.
>
>This sequential checking arises because the 'case' macroexpands to a
>'cond', where the order of the clauses IS important, that is, we are
>forced to impose an order (which we don't want) but that we know will
>affect the execution speed.
>
>To solve this problem I was thinking about a special 'case'
>macro---Let's call it 'fastcase'. This macro will receive
>a huge number of clauses and work just like 'case', but with an
>execution time independent of the number of clauses.
>
>The solution would be based on an hash-table containing lambdas for
>the forms of each clause, indexed by the constants on each
>clause. This hash-table would be stored on a local STATIC variable,
>introduced by the macroexpansion.
>
>As an example, we could have:
>
>(fastcase something
> (a form1)
> (b form2)
> (c form3))
>
>This would expand into:
>
>(LET-STATIC
> ((TABLE
> (LET ((NEW-TABLE (MAKE-HASH-TABLE :TEST #'EQL)))
> (SETF (GETHASH NEW-TABLE A) #'(LAMBDA NIL FORM1))
> (SETF (GETHASH NEW-TABLE B) #'(LAMBDA NIL FORM2))
> (SETF (GETHASH NEW-TABLE C) #'(LAMBDA NIL FORM3))
> NEW-TABLE)))
> (FUNCALL (GETHASH SOMETHING TABLE)))
>
>(I know I should have gensymed some variables but let's forget
>variable capture problems for the moment.)

Gensym is precisely the solution to your problem.

>A simple macro to do this is, then,
>
>(defmacro fastcase (exp &body clauses)
> `(let-static
> ((table (let
> ((new-table
> (make-hash-table :test #'eql)))
> ,@(mapcar #'(lambda (clause)
> `(setf (gethash new-table ,(first clause))
> #'(lambda () ,@(rest clause))))
> clauses)
> new-table)))
> (funcall (gethash ,exp table))))

Write it as this instead:

(defmacro fastcase (exp &body clauses)
(let ((table (gensym)))
`(let ((,table (let ((new-table
(make-hash-table :test #'eql)))
,@(mapcar #'(lambda (clause)
`(setf (gethash new-table ,(first clause))
#'(lambda () ,@(rest clause))))
clauses)
new-table)))
(funcall (gethash ,exp ,table)))))

One way of thinking about what a "static" variable is is that it is a
global variable whose access is limited to a specific lexical
scope. In this example, the "variable" generated by (gensym) is
accessible only during the scope of the (let ((table (gensym))) ...)
expression. Or is there some other property of a "static" variable
that your trying to capture that I'm missing?

Mike McDonald
mik...@engr.sgi.com


Antonio Leitao

unread,
Mar 21, 1996, 3:00:00 AM3/21/96
to
>>>>> "Barry" == Barry Margolin <bar...@tools.bbnplanet.com> writes:

Barry> In article <30360152...@arcana.naggum.no>,


Barry> Erik Naggum <er...@naggum.no> wrote:
>> [Antonio Leitao]
>> | We know that we could create an equivalent function IF WE COULD define
>> | the variable 'y' outside the definition of 'test1', something such as:
>> |
>> | (let ((y 0))
>> | (defun test2 ()
>> | (incf y)
>> | y))
>>
>> precisely. have you tried this? if it fails, which Lisp are you using?

I have not received Erik Naggum followup. I can't comment it, but...

Barry> I don't think he's saying that this doesn't work, just that it's not the
Barry> way he wants to do it. He would probably like to be able to define his
Barry> static variables in the scope where they're used, as is possible in other
Barry> languages.

Barry Margolin thinks correctly about what I say. I just want "to be
able to define my static variables in the scope where they're used, as


is possible in other languages."

Barry> The problem with the above is that there's no way to get LET-STATIC inside
Barry> the function definition to expand into something like it. And furthermore,
Barry> if there were another Y in the function, e.g.

Barry> (defun test2 ()
Barry> (let ((y ...))
Barry> ...
Barry> (let-static ((y 0))
Barry> (incf y)
Barry> y)))

Barry> moving the static Y outside the function wouldn't solve the
Barry> problem.

Yes, that's a point against the usual trick of defining the static
variable outside the function.

I would like to give another example that supports (I hope) my desire
of having local static variables:

This would expand into:

A simple macro to do this is, then,

(defmacro fastcase (exp &body clauses)
`(let-static
((table (let
((new-table
(make-hash-table :test #'eql)))
,@(mapcar #'(lambda (clause)
`(setf (gethash new-table ,(first clause))
#'(lambda () ,@(rest clause))))
clauses)
new-table)))
(funcall (gethash ,exp table))))

(I know I am forgetting clauses with multiple keys, etc, etc, but that
is not the point.)

Now, the problem is, again, how to define the macro LET-STATIC.

By the way, the idea of having some sort of direct access 'case' is
not mine. It is borrowed form the Explorer Lisp Machine (If I remember
correctly).

Any ideas? (about the macro LET-STATIC)


Thanks.

Antonio Leitao

Antonio Leitao

unread,
Mar 22, 1996, 3:00:00 AM3/22/96
to
>>>>> "Mike" == Mike McDonald <mik...@titian.engr.sgi.com> writes:

Mike> In article <4inl2f$r...@sparky.franz.com>,


Mike> k...@math.ufl.edu (Kelly Murray) writes:
>> In article <woka0lc...@sol.gia.ist.utl.pt>, a...@gia.ist.utl.pt (Antonio Leitao) writes:
>>>> Hello World.
>>>>
>>>> I was wondering if we can have static local variables.
>>>> As an example, we can think of the following function:
>>>>
>>>> (defun test1 ()
>>>> (let-static ((y 0))
>>>> (incf y)
>>>> y))
>>>>
>>>> (test1) -> 1
>>>> (test1) -> 2
>>>> (test1) -> 3
>>>> ...
>>>>

>> You can have let-static replace the static variable with a global gensym,


>> whose value is initialized at the time the file is loaded.
>> The let-static macro can then collect all the static references so
>> they can be initialized when the file is loaded.
>>
>>(defun compile-static-file (infile outfile)
>> (with-open-file (in infile :direction :input)
>> (with-open-file (out outfile :direction :output :if-exists :supersede)
>> (print `(eval-when (compile) (setf *static-vars* nil)) out) ;; init to nil
>> (loop for form = (read in nil nil)
>> while form do (print form out)) ;; copy input to output
>> (print `(initialize-statics) out) ;; now do initializations after expansion
>> )))
>>
>>
>>(defvar *static-vars* nil)
>>
>>(defmacro let-static ( ((var initval)) &body body) ;; only handles one var
>> (let ((new-var (gensym)))
>> (push (cons new-var initval) *static-vars*)
>> `(symbol-macrolet ((,var ,new-var)) ;; replace var with gensym.
>> (locally (declare (special ,new-var)) ;; avoid compiler warnings
>> ,@body))))
>>
>>(defmacro initialize-statics ()
>> `(progn ,@(mapcar #'(lambda (bind) `(setq ,(car bind) ,(cdr bind))) ;; set gensym
>> *static-vars*)))

Again, I can't comment correctly because I did not receive this
followup. There must be a problem with my news feeder...

Anyway, I don't like the solution, because:

1) It depends on some kind of 'batch processing'. Each file must be
digested by a preprocessor -- the compile-static-file -- before it
can't be compiled. Where is the interactive development process
that allow me to define and test a single function and that we,
Lisp Lovers, like so much?

2) It doesn't work. When the macro expands, the saved 'initval' is not
within the surrounding lexical context. And we can't solve that
because the 'initval' comes out of that lexical context at
expansion time.

3) I want to initialize a static variable when and ONLY when I define
it. To understand this point (and the previous one), consider the
following function:

(defun test (x)
(let-static ((y x))
...etc...))

We can't initialize the static variable 'y' before we apply function
'test' to some argument.

Now, the followup:

Mike> How about this instead:

Mike> (defmacro let-static ( ((var initval)) &body body) ;; only handles one var
Mike> (let ((new-var (gensym)))
Mike> `(symbol-macrolet ((,var ,new-var)) ;; replace var with gensym.
Mike> (locally (declare (special ,new-var)) ;; avoid compiler warnings
Mike> (if (not (symbol-boundp ,var))
Mike> (setq ,var ,initval))
Mike> ,@body))))

Mike> It gets rid of the global list and the compile file stuff.


Mike> Mike McDonald
Mike> mik...@engr.sgi.com

As Mike's macro stands, I have not be able to make it work. I guess
the problem is with my CommonLisp because I already had problems with
symbol-macrolet. I have managed to make it work with some
modifications but I need Mike's help to approve them.

Here is Mike's modified version:

(defmacro let-static ( ((var initval)) &body body) ;; only handles one var
(let ((new-var (gensym)))

`(symbol-macrolet ((,var ,new-var)) ;; replace var with gensym.
(locally (declare (special ,new-var)) ;; avoid compiler warnings

(if (not (boundp ',new-var))
(setq ,new-var ,initval))
,@body))))

This solution is not so much different from mine (I don't know if Mike
received my original post). Anyway, I will post it again to explain my
comments:

(defmacro let-static (vars&vals &body body)
(let ((func (gensym)))
`(let ()
(declare (special ,func))
(unless (boundp ',func)
(setq ,func
(let ,vars&vals #'(lambda () ,@body))))
(funcall ,func))))

Now, my comments:

1) Mike uses a global variable for each static var. I use a global
variable for each set of static vars.

2) Mike depends on a 'special feature': every newly created gensym is
unbound. So do I. We use this to distinguish the 'first time' from
the others. I don't know enough about ANSI CL, but I guess Mike's
expression (if (not (symbol-boundp ,var)) ...) is similar to
CLTL2's expression (if (not (boundp ',var)) ...).

3) Mike's macro doesn't work on all situations. So didn't mine. On my
first post I did put one example where things could went
wrong. Mike's solution cracks on this case. Here is a dribble:

USER(37): (defun test (x) #'(lambda () (let-static ((y x)) (incf y) y)))
TEST
USER(38): (compile 'test)
TEST
NIL
NIL
USER(39): (setf func10 (test 10))
#<Closure (:INTERNAL TEST 0) @ #x11e1a0e>
USER(40): (setf func100 (test 100))
#<Closure (:INTERNAL TEST 0) @ #x11e213e>
USER(41): (funcall func10)
11
USER(42): (funcall func10)
12
USER(43): (funcall func10)
13
USER(44): (funcall func100)
14
USER(45): ;;ERROR. This should have been 101

I guess the problem stands.

I would like to see more solutions. Or perhaps someone demonstrating
that it can't be done.

Thanks

Antonio Leitao

Mike McDonald

unread,
Mar 22, 1996, 3:00:00 AM3/22/96
to Antonio Leitao
In article <wowx4d3...@sol.gia.ist.utl.pt>,

a...@gia.ist.utl.pt (Antonio Leitao) writes:
>>>>>> "Mike" == Mike McDonald <mik...@titian.engr.sgi.com> writes:

>Now, the followup:
>
> Mike> How about this instead:

>Here is Mike's modified version:


>
>(defmacro let-static ( ((var initval)) &body body) ;; only handles one var
> (let ((new-var (gensym)))
> `(symbol-macrolet ((,var ,new-var)) ;; replace var with gensym.
> (locally (declare (special ,new-var)) ;; avoid compiler warnings
> (if (not (boundp ',new-var))
> (setq ,new-var ,initval))
> ,@body))))

>Now, my comments:


>
>1) Mike uses a global variable for each static var. I use a global
> variable for each set of static vars.
>
>2) Mike depends on a 'special feature': every newly created gensym is
> unbound. So do I. We use this to distinguish the 'first time' from
> the others. I don't know enough about ANSI CL, but I guess Mike's
> expression (if (not (symbol-boundp ,var)) ...) is similar to
> CLTL2's expression (if (not (boundp ',var)) ...).

My mind's going... I had thought that boundp got renamed in one of
the standards to symbol-boundp so it's naming would be consistent with
all of the other symbol-* functions. The missing ' is a bug in my version.

>3) Mike's macro doesn't work on all situations. So didn't mine. On my
> first post I did put one example where things could went
> wrong. Mike's solution cracks on this case. Here is a dribble:
>
>USER(37): (defun test (x) #'(lambda () (let-static ((y x)) (incf y) y)))
>TEST
>USER(38): (compile 'test)
>TEST
>NIL
>NIL
>USER(39): (setf func10 (test 10))
>#<Closure (:INTERNAL TEST 0) @ #x11e1a0e>
>USER(40): (setf func100 (test 100))
>#<Closure (:INTERNAL TEST 0) @ #x11e213e>
>USER(41): (funcall func10)
>11
>USER(42): (funcall func10)
>12
>USER(43): (funcall func10)
>13
>USER(44): (funcall func100)
>14
>USER(45): ;;ERROR. This should have been 101

Are you sure? Well, I guess you are since you get to define the
problem. :-) I don't believe I've ever seen a definition of how a
closure and a static variable should act. One reasonable definition,
reasonable to me anyway, would be that all the closures should refer
to the same static variable, not a separate copy for each instance of
a closure. That's what's happening here.

Mike McDonald
mik...@engr.sgi.com


Joao Cachopo

unread,
Mar 24, 1996, 3:00:00 AM3/24/96
to
>>>>> "Mike" == Mike McDonald <mik...@titian.engr.sgi.com> writes:

Mike> In article <wovijyw...@sol.gia.ist.utl.pt>,


Mike> a...@gia.ist.utl.pt (Antonio Leitao) writes:

>>
>> The solution would be based on an hash-table containing lambdas
>> for the forms of each clause, indexed by the constants on each
>> clause. This hash-table would be stored on a local STATIC
>> variable, introduced by the macroexpansion.
>>

Mike> Write it as this instead:

Mike> (defmacro fastcase (exp &body clauses)
Mike> (let ((table (gensym)))
Mike> `(let ((,table (let ((new-table
Mike> (make-hash-table :test #'eql)))
Mike> ,@(mapcar #'(lambda (clause)
Mike> `(setf (gethash new-table ,(first clause))
Mike> #'(lambda () ,@(rest clause))))
Mike> clauses)
Mike> new-table)))
Mike> (funcall (gethash ,exp ,table)))))

Mike> One way of thinking about what a "static" variable is is that
Mike> it is a global variable whose access is limited to a specific
Mike> lexical scope. In this example, the "variable" generated by
Mike> (gensym) is accessible only during the scope of the (let
Mike> ((table (gensym))) ...) expression. Or is there some other
Mike> property of a "static" variable that your trying to capture
Mike> that I'm missing?

I think you missed the point entirely.

Your version of "fastcase" expands into something which creates and
initialiazes the hash-table every time the code is evaluated.

The "let-static" macro (I still don't know whether it is possible to
build one or not, but I believe it is not) would allow the table to be
created the first time it was needed and used since after.


--
Joao Cachopo * http://www.gia.ist.utl.pt/~jcachopo

Antonio Leitao

unread,
Mar 27, 1996, 3:00:00 AM3/27/96
to Ingemar Hulthage
>>>>> "Ingemar" == Ingemar Hulthage <hult...@hollywood.cinenet.net> writes:

Ingemar> Is this what you need ?

Ingemar> (let ((static 0))
Ingemar> (defun foo () (incf static))
Ingemar> )

Ingemar> Ingemar Hulthage

Yes and No.

Yes, I want lexical variables with indefinite extent.
No, I don't want to wrap a lambda around a function because I want
'let-static' to be a macro and macros can't wrap code outside the macro
call.

On other words, I want to write

(defun foo-2 ()
(let-static ((static 0))
(incf static)))

and automagically get a function very similar to your's 'foo'.

let-static _must_ be a macro (unless you want to rewrite the
evaluator---maybe its the only solution.)


I would be very happy if someone could answer about the feasibility of
this macro (Peter Norvig, Steele, Paul Graham, Sussman and all my other Lisp heroes,
where are you?)


Thanks in advance

Antonio Leitao

Barry Margolin

unread,
Mar 28, 1996, 3:00:00 AM3/28/96
to
In article <wxka0az...@marte.gia.ist.utl.pt>,

Joao Cachopo <jcac...@gia.ist.utl.pt> wrote:
>The "let-static" macro (I still don't know whether it is possible to
>build one or not, but I believe it is not) would allow the table to be
>created the first time it was needed and used since after.

I don't think it's possible to write LET-STATIC. The usual way that
problems like this are solved is by having the macro create a global,
gensym'ed variable, e.g.

(defmacro fastcase (exp &body clauses)

(let ((table (gensym)))
`(locally (declare (special ,table))
(unless (boundp ',table)
(setq ,table (make-hash-table :test #'eql))
,@(mapcar #'(lambda (clause)
`(setf (gethash ,table (first clause))
#'(lambda () ,@(rest clause))))))
(funcall (gethash ,exp ,table)))))

Richard Mlynarik

unread,
Apr 2, 1996, 3:00:00 AM4/2/96
to
This is actually a pretty interesting question.

Clearly there is a "local static"-creation function present in the
Lisp loader (eg for the creation of constant strings, vectors, lists,
etc) but there is no real user access to this functionality.

One could think of (define (foo) "bar")
as (define (foo) (%load-time-special-form (make-constant-string #\b #\a #\r)))

The solution posted by Barmar and Mike MacDonald of using a
macroexpand-time gensym to memoise a once-only call to a
data-structure creation routine is a way of faking around the lack of
the above, by relying again on the loader to be able to create new
embedded composite structure (in this case, an uninterned symbol) in a
way which isn't available to the Lisp programmer.

Note that one -could- define `%load-time-special-form' in terms of
such a memoising interface, begging the question of whether constants
are required to be created at load-time (whatever that means) or
whether cons-on-demand is acceptable.

Lawrence G. Mayka

unread,
Apr 4, 1996, 3:00:00 AM4/4/96
to
m...@adoc.xerox.com (Richard Mlynarik) wrote:

>Clearly there is a "local static"-creation function present in the
>Lisp loader (eg for the creation of constant strings, vectors, lists,
>etc) but there is no real user access to this functionality.
>
>One could think of (define (foo) "bar")
>as (define (foo) (%load-time-special-form (make-constant-string #\b #\a #\r)))

See LOAD-TIME-VALUE, in the ANSI spec or CLtL2. For example, one can
write

(let ((cell (load-time-value (make-array nil :initial-element 0))))
(symbol-macrolet ((static-var (aref cell)))
...))

and then treat STATIC-VAR as a general-purpose static variable whose
initial value is 0.
Lawrence G. Mayka
LGM...@msn.com

0 new messages