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

Creepy special vars in interactive development

64 views
Skip to first unread message

Stefan Mandl

unread,
May 19, 2012, 6:11:41 AM5/19/12
to
Dear Group,

below there is a transcript of a (simplified) interactive
session I recently had.

; ----------------------------------------------------
CL-USER> (defun test ()
(let ((l '(1 2 3)))
(lambda ()
(reverse l))))
TEST
CL-USER> (defparameter l (test))
L
CL-USER> (funcall l)
(3 2 1)
CL-USER> (defun test ()
(let ((l '(1 2 3)))
(lambda ()
(reverse l))))
STYLE-WARNING: redefining COMMON-LISP-USER::TEST in DEFUN
TEST
CL-USER> (setf l (test))
#<FUNCTION (LAMBDA () :IN TEST) {BC3DF4D}>
CL-USER> (funcall l)
; Evaluation aborted on #<TYPE-ERROR expected-type: SEQUENCE
datum: #<FUNCTION (LAMBDA () :IN TEST) {BC3DF4D}>>.
CL-USER>
; ----------------------------------------------------

The problem seems to be that the first defparameter turns l
into a special variable which in the subsequent redefinition of
test is treated as such.
I find this behavior rather annoying for interactive development.


So,
- is there a way to "unspecialize" variables?
- What else could be used instead of defparameter/defvar to create top-
level bindings?
- How do you get around this kind of issues in your daily coding?

Any hints appreciated!

Best,
Stefan

Pascal J. Bourguignon

unread,
May 19, 2012, 6:18:15 AM5/19/12
to
Why do you think we told you to use earmuffs for your special variables?


> So,
> - is there a way to "unspecialize" variables?

No. But you can unintern the symbol, and re-read all definitions that
used the old symbol. This is somewhat violent.



> - What else could be used instead of defparameter/defvar to create top-
> level bindings?

define-symbol-macro.

This allows to define global lexical variables. Google for defglobal or
deflexical.


> - How do you get around this kind of issues in your daily coding?

We just use the fine earmuff convention!

(defun test ()
(let ((l '(1 2 3)))
(lambda () (reverse l))))

(defparameter *l* (test))

(funcall *l*)

(defun test ()
(let ((l '(1 2 3)))
(lambda () (reverse l))))


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

Stefan Mandl

unread,
May 19, 2012, 6:42:04 AM5/19/12
to
>
> Why do you think we told you to use earmuffs for your special variables?
>

That's true. Should have done that. Maybe I was too much inspired by
Doug Hoyte's book where he argues against the earmuffs, but for situations
like mine they are probably the best solution. Thanks!

>
>> So,
>> - is there a way to "unspecialize" variables?
>
> No. But you can unintern the symbol, and re-read all definitions that
> used the old symbol. This is somewhat violent.

Agreed and a bit unsafe as in general you do not get an error message
that tells you something's wrong, you might just get strange behavior.

>> - What else could be used instead of defparameter/defvar to create top-
>> level bindings?
>
> define-symbol-macro.

Ah, ok, never would have thought of using them in that context. Very nice.

> This allows to define global lexical variables. Google for defglobal or
> deflexical.

SBCl does not have deflexical, but in fact has a defglobal.
I just don't think it's useful for interactive development as it
prevents the redefinition of the function:

; ------------------------
CL-USER> (defun test ()
(let ((l '(1 2 3)))
(lambda ()
(reverse l))))
TEST
CL-USER> (defglobal l (test))
L
CL-USER> (funcall l)
(3 2 1)
CL-USER> (defun test ()
(let ((l '(a b c)))
(lambda ()
(reverse l))))
; in: DEFUN TEST
; (LET ((L '(A B C)))
; (LAMBDA () (REVERSE L)))
;
; caught ERROR:
; L names a global lexical variable, and cannot be used as a local
variable.
;
; compilation unit finished
; caught 1 ERROR condition
STYLE-WARNING: redefining COMMON-LISP-USER::TEST in DEFUN
TEST
CL-USER> (funcall l)
(3 2 1)
CL-USER>
; ------------------------

>> - How do you get around this kind of issues in your daily coding?
>
> We just use the fine earmuff convention!

Plain simple. Really should have though of that, thanks!

Well, but now I have to hope that nobody else uses a variable named *l*
anywhere else in the code I'm including, right? I know, that's what
packages are for, but one never knows.

I'll probably take a look at your define-symbol-macro suggestion.

Best,
Stefan

Elias Mårtenson

unread,
May 19, 2012, 6:46:06 AM5/19/12
to
On Saturday, 19 May 2012 18:42:04 UTC+8, Stefan Mandl wrote:

> >> - How do you get around this kind of issues in your daily coding?
> >
> > We just use the fine earmuff convention!
>
> Plain simple. Really should have though of that, thanks!
>
> Well, but now I have to hope that nobody else uses a variable named *l*
> anywhere else in the code I'm including, right? I know, that's what
> packages are for, but one never knows.

If you're working in your own package, then you'll definitely know. :-)

Stefan Mandl

unread,
May 19, 2012, 6:50:22 AM5/19/12
to
You're right of course ... time for some more discipline on my side ;-)

Pascal J. Bourguignon

unread,
May 19, 2012, 7:59:10 AM5/19/12
to
Furthermore, even if they use the same symbol as you, the only collision
is in the global value.

If you (or them) write instead:

(let ((*l* (test)))
(funcall *l*))

and the function returned by TEST doesn't call their function, then
there's no interference.

WJ

unread,
May 19, 2012, 8:11:52 AM5/19/12
to
Racket:

(define (test)
(let ((l '(1 2 3)))
(lambda ()
(reverse l))))

(define l (test))

(l)

=> '(3 2 1)

(define (test)
(let ((l '(a b c)))
(lambda ()
(reverse l))))

(define l (test))

(l)

=> '(c b a)

Stefan Mandl

unread,
May 19, 2012, 9:36:29 AM5/19/12
to
Again, thanks a lot!

Just for reference, I found a hint at sb-int:clear-info
in Maxima's source code.

Then one could define:

(defun make-unspecial (symbol)
(sb-int:clear-info :variable :kind symbol)
symbol)


(defun test ()
(let ((l '(1 2 3)))
(lambda ()
(reverse l))))


and use it in the following way:

;; --------------------------------
CL-USER> (defparameter l (test))
L
CL-USER> (funcall l)
(3 2 1)
CL-USER> (make-unspecial 'l)
L
CL-USER> (defun test ()
(let ((l '(a b c)))
(lambda ()
(reverse l))))

STYLE-WARNING: redefining COMMON-LISP-USER::TEST in DEFUN
TEST
CL-USER> (defparameter l (test))
L
CL-USER> (funcall l)
(C B A)
CL-USER>
;; --------------------------------

But that's of course really hairy stuff.
I'll stick to your and Elias' suggestion on
the combination of packages and earmuffs.

So again, thanks a lot.

Best,
Stefan

Stefan Mandl

unread,
May 19, 2012, 9:43:43 AM5/19/12
to
> Racket:
>
> (define (test)
> (let ((l '(1 2 3)))
> (lambda ()
> (reverse l))))
>
> (define l (test))
>
> (l)
>
> => '(3 2 1)
>
> (define (test)
> (let ((l '(a b c)))
> (lambda ()
> (reverse l))))
>
> (define l (test))
>
> (l)
>
> => '(c b a)

Yeah, I know, there are no special variables in Scheme.

I learned quite some Scheme in the 90s, maybe that's why they are so hard
for me today ;-)

Best,
Stefan

Zach Beane

unread,
May 19, 2012, 9:44:18 AM5/19/12
to
Stefan Mandl <Stefa...@web.de> writes:

> Just for reference, I found a hint at sb-int:clear-info
> in Maxima's source code.

SB-INT is an internal package, and if you use functions from it in your
code, it might break any time SBCL is updated.

Zach

Stefan Mandl

unread,
May 19, 2012, 9:52:38 AM5/19/12
to
Well I didn't intend to use it, just wanted to point out that it's there.

Let's hope that Maxima does not break ;-)

On the other hand, when I think about it: it's a public symbol (:) not
(::), if that's the right terminology, so I guess it's quite stable.

But of course you are right, one should minimize the use of non-portable
extensions as much as possible, even more so when there's no real need
for them like it is for me in this thread.

Best,
Stefan

Zach Beane

unread,
May 19, 2012, 4:44:38 PM5/19/12
to
Stefan Mandl <Stefa...@web.de> writes:

> On Sat, 19 May 2012 09:44:18 -0400, Zach Beane wrote:
>
>> Stefan Mandl <Stefa...@web.de> writes:
>>
>>> Just for reference, I found a hint at sb-int:clear-info in Maxima's
>>> source code.
>>
>> SB-INT is an internal package, and if you use functions from it in your
>> code, it might break any time SBCL is updated.
>
> Well I didn't intend to use it, just wanted to point out that it's there.
>
> Let's hope that Maxima does not break ;-)
>
> On the other hand, when I think about it: it's a public symbol (:) not
> (::), if that's the right terminology, so I guess it's quite stable.

That's so other parts of SBCL can use it, not so external users can use
it.

* (documentation (find-package 'sb-int) t)

"private: miscellaneous unsupported extensions to the ANSI
spec. Much of the stuff in here originated in CMU CL's EXTENSIONS
package and is retained, possibly temporariliy, because it might be
used internally."

Zach

Stefan Mandl

unread,
May 19, 2012, 5:43:31 PM5/19/12
to
> That's so other parts of SBCL can use it, not so external users can use
> it.
>
> * (documentation (find-package 'sb-int) t)
>
> "private: miscellaneous unsupported extensions to the ANSI spec.
> Much of the stuff in here originated in CMU CL's EXTENSIONS package
> and is retained, possibly temporariliy, because it might be used
> internally."
>
>
I see and I hear you.Promise: I will not use it.
Again, thanks a lot, especially for quicklisp. It's really amazing what
you are doing; Congratulations, I'm a big fan!

stm.

RG

unread,
May 21, 2012, 1:55:30 AM5/21/12
to

Björn Lindberg

unread,
May 21, 2012, 5:19:07 AM5/21/12
to
Stefan Mandl <Stefa...@web.de> writes:

> The problem seems to be that the first defparameter turns l
> into a special variable which in the subsequent redefinition of
> test is treated as such.
> I find this behavior rather annoying for interactive development.

> - How do you get around this kind of issues in your daily coding?

I tend to use a naming convention for such interactive variables, namely
the prefix ">>>". Your variable would then be called >>>l. It clearly
identifies these variables as interactive and not part of the code you
are working on. This particular prefix stands out, is quick to type and
allows you to tab complete on >>> to see which variables you have
defined.


Björn Lindberg

Stefan Mandl

unread,
May 21, 2012, 3:42:53 PM5/21/12
to
>
> http://www.flownet.com/ron/specials.pdf
> http://blog.rongarret.info/2009/08/global-variables-done-right.html

Wow, I was obviously only dealing with the tip of the iceberg.
Thanks a lot for the hints!

stm

Stefan Mandl

unread,
May 21, 2012, 3:43:54 PM5/21/12
to

> I tend to use a naming convention for such interactive variables, namely
> the prefix ">>>". Your variable would then be called >>>l. It clearly
> identifies these variables as interactive and not part of the code you
> are working on. This particular prefix stands out, is quick to type and
> allows you to tab complete on >>> to see which variables you have
> defined.

That's a very clever trick. Thanks a lot!

stm
0 new messages