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

Binding in defparameter versus let

102 views
Skip to first unread message

paris...@gmail.com

unread,
Sep 9, 2012, 5:05:45 PM9/9/12
to
Today I was cleaning up some code and made a mistake.

The code was originally like this
(defparameter *baz* (list 'a #'(lambda()(third *baz*)) 'c))

Now *baz* is bound early enough that the lambda works properly.

It failed when I moved it into a function:

(defun foo ()
(let ((bar (list 'a #'(lambda()(third bar)) 'c)))
(print bar)))

The binding for let doesn't occur until running, so (third bar) is unbound.

This can be easily remedied with:
(defun foo ()
(let ((bar))
(setf bar (list 'a #'(lambda()(third bar)) 'c))
(print bar)))

Are there neater ways to do the same thing?

Bob

Pascal J. Bourguignon

unread,
Sep 9, 2012, 5:11:02 PM9/9/12
to
No. Neither LET nor LET* can help here.

You could also set the function in the list like this:

(let ((bar (list 'a nil 'c)))
(setf (second bar) (lambda () (third bar)))
bar)




--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.

Barry Margolin

unread,
Sep 9, 2012, 5:30:14 PM9/9/12
to
In article <10e817c1-f4f5-4033...@googlegroups.com>,
paris...@gmail.com wrote:

> Today I was cleaning up some code and made a mistake.
>
> The code was originally like this
> (defparameter *baz* (list 'a #'(lambda()(third *baz*)) 'c))
>
> Now *baz* is bound early enough that the lambda works properly.

The issue isn't when it's bound, it's that it's bound in the same scope
that the variable is looked up in -- they're both in the global scope.

>
> It failed when I moved it into a function:
>
> (defun foo ()
> (let ((bar (list 'a #'(lambda()(third bar)) 'c)))
> (print bar)))
>
> The binding for let doesn't occur until running, so (third bar) is unbound.

No, the issue is that the scope of the binding is the LET body, which
doesn't include the initial value expression.

>
> This can be easily remedied with:
> (defun foo ()
> (let ((bar))
> (setf bar (list 'a #'(lambda()(third bar)) 'c))
> (print bar)))
>
> Are there neater ways to do the same thing?

(defun foo ()
(locally (declare (special bar))
(let ((bar (list 'a #'(lambda () (third bar)) 'c)))
(print bar))))

In Scheme you could do what you want using LETREC, but Common Lisp
doesn't have this. Since it's usually used for defining local
functions, and Common Lisp doesn't use LET for defining local functions
(because of the separate function and value namespaces), we achieve this
using FLET and LABELS -- the latter allows for recursion. But we don't
have a LABELS-equivalent for LET.

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Pascal Costanza

unread,
Sep 9, 2012, 6:08:58 PM9/9/12
to
It doesn't take a lot to have letrec in Common Lisp:

(defmacro letrec ((&rest bindings) &body body)
`(let ,(mapcar #'first bindings)
(setq ,@(apply #'append bindings))
,@body))

> (letrec ((bar (list 'a (lambda () (third bar)) 'c)))
(print bar)
(print (funcall (second bar))))

(A #<anonymous interpreted function 200C56CA> C)
C


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
The views expressed are my own, and not those of my employer.

RG

unread,
Sep 9, 2012, 7:30:44 PM9/9/12
to
In article <barmar-0B446B....@news.eternal-september.org>,
But adding it is an elementary exercise.

? (defmacro letrec (var value &body body)
`(let (,var) (setf ,var ,value) ,@body))
LETREC
? (letrec bar (list 'a #'(lambda()(third bar)) 'c) (funcall (second
bar)))
C

Extending to multiple bindings left as an exercise.

But I have to wonder why anyone would want to do this.

rg

Kaz Kylheku

unread,
Sep 9, 2012, 10:57:36 PM9/9/12
to
On 2012-09-09, paris...@gmail.com <paris...@gmail.com> wrote:
> Today I was cleaning up some code and made a mistake.
>
> The code was originally like this
> (defparameter *baz* (list 'a #'(lambda()(third *baz*)) 'c))
>
> Now *baz* is bound early enough that the lambda works properly.

Not necessarily. *baz* is a free variable (i.e. a symbol with no apparent
binding) inside the lambda (at least the first time that defparameter is
evaluated), so it gets treated as a dynamic variable. Later, when the
lambda is called, that dynamic variable resolves to the special *baz*.

> It failed when I moved it into a function:
>
> (defun foo ()
> (let ((bar (list 'a #'(lambda()(third bar)) 'c)))
> (print bar)))

Here, the variable bar is also free. It can never refer to the bar which
is being bound, because that variable is lexical and free references can only
ever resolve to dynamic variables. This has to do with scoping, not with timing.

It will work if you make bar into a dynamic variable.

(defun foo ()
(let ((bar (list 'a #'(lambda()(third bar)) 'c)))
(declare (special bar))
(funcall (second bar))))

(foo) -> C

Take out the declare and it doesn't work due to bar being unbound.

Björn Lindberg

unread,
Sep 13, 2012, 8:12:03 AM9/13/12
to
You can achieve the *same effect* by not going through the variable at
all:

(defun foo ()
(let ((bar #1=(list 'a #'(lambda () (third #1#)) 'c)))
(funcall (second bar))))

(foo) => C


Bj�rn Lindberg

Pascal J. Bourguignon

unread,
Sep 13, 2012, 8:32:51 AM9/13/12
to
This is wrong.


cl-user> (setf *print-circle* nil
*print-level* 10)
10
cl-user> '(defun foo ()
(let ((bar #1=(list 'a #'(lambda () (third #1#)) 'c)))
(funcall (second bar))))
(defun foo nil (let ((bar (list 'a #'(lambda nil (third (list 'a #'# 'c))) 'c)))
(funcall (second bar))))

You're allocating a new list everytime you're calling the function!

Björn Lindberg

unread,
Sep 14, 2012, 4:26:57 AM9/14/12
to
"Pascal J. Bourguignon" <p...@informatimago.com> writes:

> You're allocating a new list everytime you're calling the function!

Which is what he did in his original function too.


Bj�rn Lindberg

Pascal J. Bourguignon

unread,
Sep 14, 2012, 7:01:43 AM9/14/12
to
bj...@runa.se (Bj�rn Lindberg) writes:

Furthermore, you have a cycle in the code, not in literal data, so it
won't be compiled in most implementation (infinite recursion in the
compilers).

Pascal J. Bourguignon

unread,
Sep 14, 2012, 7:02:03 AM9/14/12
to
bj...@runa.se (Bj�rn Lindberg) writes:

No, I mean the anonymous function.

parisnight

unread,
Sep 14, 2012, 10:29:11 AM9/14/12
to
The explanations provided by Barry and Kaz helped me to understand the
underlying reason for the behavior. I must also thank others for
suggesting other approaches and their various details.

(enlightened Bob)
0 new messages