From: Reinhard Gantar
Subject: Closures: I'm not getting it
Date: 
Message-ID: <2gn80oF4p9q7U1@uni-berlin.de>
Dear c.l.l.,

in David Lamkins "Successful Lisp", Chapter 11,
we read a simple example for closures:

? (let ((e 1))
     (defun closure-1 () e))

? (closure-1)
gives 1.

I know what this does (creating a cons for storing 1,
binding it to e). The variable e is not visible because
the scope is limited to the inside of the let.
So far so good. What I don't understand is why this
example works. According to what I understood, the
cons with the 1, together with its binding e should
be gc-ed as soon as closure-1 is gc-ed, and
closure-1 should be gc-ed as soon as the let-form is
gc-ed. Since the let-form has been created by the
REPL, it should be gc-ed IMMEDIATELY, just as a
form like

? (+ 42 23)
-> 67

is gc-ed right after the evaluation (or is it?)
What's wrong here? Is it a mere convention that
(let ...)s or (defun ...)s don't get gc-ed right away? This
cruft does not look like my dear lisp at all.
I guess I'm missing something, but what?

Kind regards
Gantar

Link to Chapter 11:
http://www.psg.com/~dlamkins/sl/chapter11.html




http://www.psg.com/~dlamkins/sl/contents.html#ch11

From: William Bland
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <pan.2004.05.15.19.15.23.459996@abstractnonsense.com>
On Sat, 15 May 2004 21:05:58 +0200, Reinhard Gantar wrote:

> Dear c.l.l.,
> 
> in David Lamkins "Successful Lisp", Chapter 11,
> we read a simple example for closures:
> 
> ? (let ((e 1))
>      (defun closure-1 () e))
> 
> ? (closure-1)
> gives 1.
> 
> I know what this does (creating a cons for storing 1,
> binding it to e). The variable e is not visible because
> the scope is limited to the inside of the let.
> So far so good. What I don't understand is why this
> example works. According to what I understood, the
> cons with the 1, together with its binding e should
> be gc-ed as soon as closure-1 is gc-ed, and
> closure-1 should be gc-ed as soon as the let-form is
> gc-ed. Since the let-form has been created by the
> REPL, it should be gc-ed IMMEDIATELY, just as a
> form like
> 
> ? (+ 42 23)
> -> 67
> 
> is gc-ed right after the evaluation (or is it?)
> What's wrong here? Is it a mere convention that
> (let ...)s or (defun ...)s don't get gc-ed right away? This
> cruft does not look like my dear lisp at all.
> I guess I'm missing something, but what?

Actually your reasoning would be pretty much correct if this was Scheme,
where you would use define instead of defun, and the binding of closure-1
would only exist within the let so e and closure-1 would go away.  But in
Common Lisp, defun creates a new top-level binding (i.e. it is visible
outside of the let), and top-level bindings don't just vanish.

I may have missed something or got it all wrong - I'm still a Lisp newbie.

Cheers,
	Bill.
-- 
Dr. William Bland.
It would not be too unfair to any language to refer to Java as a
stripped down Lisp or Smalltalk with a C syntax.   (Ken Anderson).
From: Reinhard Gantar
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gna96F4k8pjU1@uni-berlin.de>
William Bland wrote:
<SNIPPO />

> 
> Actually your reasoning would be pretty much correct if this was Scheme,
> where you would use define instead of defun, and the binding of closure-1
> would only exist within the let so e and closure-1 would go away.  But in
> Common Lisp, defun creates a new top-level binding (i.e. it is visible
> outside of the let), and top-level bindings don't just vanish.
> 
> I may have missed something or got it all wrong - I'm still a Lisp newbie.

What you are saying not only makes sense, it is
what Pascal Costanza and Svein Ove Aas (next posts) are saying, too.

The fact that the defun'ed function closure-1 is visible
outside the (let ...) should have clued me in.

Thanks
Gantar
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <svupc.161663$f_5.83295@lakeread01>
Reinhard Gantar wrote:

> William Bland wrote:
> <SNIPPO />
> 
>>
>> Actually your reasoning would be pretty much correct if this was Scheme,
>> where you would use define instead of defun, and the binding of closure-1
>> would only exist within the let so e and closure-1 would go away.  But in
>> Common Lisp, defun creates a new top-level binding (i.e. it is visible
>> outside of the let), and top-level bindings don't just vanish.
>>
>> I may have missed something or got it all wrong - I'm still a Lisp 
>> newbie.
> 
> 
> What you are saying not only makes sense, it is
> what Pascal Costanza and Svein Ove Aas (next posts) are saying, too.
> 
> The fact that the defun'ed function closure-1 is visible
> outside the (let ...) should have clued me in.

If you do want a local function, see 'labels'.  It's like a 'let' for 
closures.
From: Pascal Costanza
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <c85qi5$9jf$1@newsreader2.netcologne.de>
Reinhard Gantar wrote:

> Dear c.l.l.,
> 
> in David Lamkins "Successful Lisp", Chapter 11,
> we read a simple example for closures:
> 
> ? (let ((e 1))
>     (defun closure-1 () e))
> 
> ? (closure-1)
> gives 1.
> 
> I know what this does (creating a cons for storing 1,
> binding it to e). The variable e is not visible because
> the scope is limited to the inside of the let.
> So far so good. What I don't understand is why this
> example works. According to what I understood, the
> cons with the 1, together with its binding e should
> be gc-ed as soon as closure-1 is gc-ed, and
> closure-1 should be gc-ed as soon as the let-form is
> gc-ed. Since the let-form has been created by the
> REPL, it should be gc-ed IMMEDIATELY, just as a
> form like
> 
> ? (+ 42 23)
> -> 67
> 
> is gc-ed right after the evaluation (or is it?)
> What's wrong here? Is it a mere convention that
> (let ...)s or (defun ...)s don't get gc-ed right away? This
> cruft does not look like my dear lisp at all.
> I guess I'm missing something, but what?

You are missing that DEFUN is specified to create a function that is 
globally accessible via the name given in the DEFUN form. Things that 
are accessible via global names are not gc'ed. That's all.

If you want to build a function that can be immediately gc'ed, use 
LAMBDA to create an anonymous function:

(let ((e 1))
   #'(lambda () e))

However, in order to be able to use it, you have to assign it to a 
variable, and then you are circumventing garbage collection again, at 
least until the variable goes out of scope. However, that's what you 
want, right?

Pascal

P.S.: Common Lisp listeners bind the results of the previous three 
evaluated forms in the variables *, ** and *** respectively. So after 
typing in the above form, you can in fact say the following.

(funcall *)

...which should yield 1.

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Reinhard Gantar
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gnaioF4rjnbU1@uni-berlin.de>
Pascal Costanza wrote:
> 
<SNIPPO />

> 
> 
> You are missing that DEFUN is specified to create a function that is 
> globally accessible via the name given in the DEFUN form. Things that 
> are accessible via global names are not gc'ed. That's all.
> 

Yes, I should have thought of that (after all, closure-1 IS
visible outside the (let ...).

> If you want to build a function that can be immediately gc'ed, use 
> LAMBDA to create an anonymous function:
> 
> (let ((e 1))
>   #'(lambda () e))
> 
Makes sense.
> However, in order to be able to use it, you have to assign it to a 
> variable, and then you are circumventing garbage collection again, at 
> least until the variable goes out of scope. However, that's what you 
> want, right?

This is what makes a closure tick. I understand it as a
way to encapsulate data (like a private member in C++ and
a getter/setter, although there seems to be more to it.
Closure are all the rage in the lisp-world (-:    )

> 
> Pascal
> 
> P.S.: Common Lisp listeners bind the results of the previous three 
> evaluated forms in the variables *, ** and *** respectively. So after 
> typing in the above form, you can in fact say the following.
> 
> (funcall *)
> 
> ...which should yield 1.

Sounds perlish to me (:

Thanks
Gantar


> 
From: Svein Ove Aas
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <niwpc.1770$eH3.41979@news4.e.nsc.no>
Reinhard Gantar wrote:

> Pascal Costanza wrote:
>> 
> <SNIPPO />
> 
>> 
>> 
>> You are missing that DEFUN is specified to create a function that is
>> globally accessible via the name given in the DEFUN form. Things that
>> are accessible via global names are not gc'ed. That's all.
>> 
> 
> Yes, I should have thought of that (after all, closure-1 IS
> visible outside the (let ...).
> 
>> If you want to build a function that can be immediately gc'ed, use
>> LAMBDA to create an anonymous function:
>> 
>> (let ((e 1))
>>   #'(lambda () e))
>> 
> Makes sense.
>> However, in order to be able to use it, you have to assign it to a
>> variable, and then you are circumventing garbage collection again, at
>> least until the variable goes out of scope. However, that's what you
>> want, right?
> 
> This is what makes a closure tick. I understand it as a
> way to encapsulate data (like a private member in C++ and
> a getter/setter, although there seems to be more to it.
> Closure are all the rage in the lisp-world (-:    )

You can think of a closure as a lightweight object with only a single
function to its name, and less to write. This ignores the fact that you
can define local functions inside the closure, and have said single
function call them based on its first argument; as a matter of fact, the
CLOS is (or could be, at least) entirely written in Lisp, presumably with
closures.

Take a look at this:

(let (slot-1 slot-2 slot-3
     (fn-1 (lambda (x) (+ slot-1 x)))
      ..etc..)
  (defun object-1 (function &rest args)
     (ecase function
        'fn-1 (apply fn-1 args)
        'fn-2 (apply fn-2 args))))

See what I mean?
Now, throw in a "defclass" macro, and...
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <7xwpc.162139$f_5.107889@lakeread01>
Svein Ove Aas wrote:

> Reinhard Gantar wrote:
> 
> 
>>Pascal Costanza wrote:
>>
>><SNIPPO />
>>
>>>
>>>You are missing that DEFUN is specified to create a function that is
>>>globally accessible via the name given in the DEFUN form. Things that
>>>are accessible via global names are not gc'ed. That's all.
>>>
>>
>>Yes, I should have thought of that (after all, closure-1 IS
>>visible outside the (let ...).
>>
>>
>>>If you want to build a function that can be immediately gc'ed, use
>>>LAMBDA to create an anonymous function:
>>>
>>>(let ((e 1))
>>>  #'(lambda () e))
>>>
>>
>>Makes sense.
>>
>>>However, in order to be able to use it, you have to assign it to a
>>>variable, and then you are circumventing garbage collection again, at
>>>least until the variable goes out of scope. However, that's what you
>>>want, right?
>>
>>This is what makes a closure tick. I understand it as a
>>way to encapsulate data (like a private member in C++ and
>>a getter/setter, although there seems to be more to it.
>>Closure are all the rage in the lisp-world (-:    )
> 
> 
> You can think of a closure as a lightweight object with only a single
> function to its name, and less to write. This ignores the fact that you
> can define local functions inside the closure, and have said single
> function call them based on its first argument; as a matter of fact, the
> CLOS is (or could be, at least) entirely written in Lisp, presumably with
> closures.
> 
> Take a look at this:
> 
> (let (slot-1 slot-2 slot-3
>      (fn-1 (lambda (x) (+ slot-1 x)))
>       ..etc..)
>   (defun object-1 (function &rest args)
>      (ecase function
>         'fn-1 (apply fn-1 args)
>         'fn-2 (apply fn-2 args))))
> 
> See what I mean?
> Now, throw in a "defclass" macro, and...

You can certainly have more than one function closed over the same 
environment.

(let (let-forms)
   (defun f1 (...) ...)
   (defun f2 (...) ...))

or:

(let (let-forms)
   (list
     (lambda (...) ...)
     (lambda (...) ...)))

or how about this:

(setq methods
   (let (let-forms)
     (list
       (cons 'f1 (lambda (...) ...))
       (cons 'f2 (lambda (...) ...)))))
(funcall (cdr (assoc 'f1 methods)) ...)
From: Svein Ove Aas
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2Awpc.1731$RL3.36556@news2.e.nsc.no>
Ari Johnson wrote:

> Svein Ove Aas wrote:
> 
>> Reinhard Gantar wrote:
>> 
>> 
>>>Pascal Costanza wrote:
>>>
>>><SNIPPO />
>>>
>>>>
>>>>You are missing that DEFUN is specified to create a function that is
>>>>globally accessible via the name given in the DEFUN form. Things that
>>>>are accessible via global names are not gc'ed. That's all.
>>>>
>>>
>>>Yes, I should have thought of that (after all, closure-1 IS
>>>visible outside the (let ...).
>>>
>>>
>>>>If you want to build a function that can be immediately gc'ed, use
>>>>LAMBDA to create an anonymous function:
>>>>
>>>>(let ((e 1))
>>>>  #'(lambda () e))
>>>>
>>>
>>>Makes sense.
>>>
>>>>However, in order to be able to use it, you have to assign it to a
>>>>variable, and then you are circumventing garbage collection again, at
>>>>least until the variable goes out of scope. However, that's what you
>>>>want, right?
>>>
>>>This is what makes a closure tick. I understand it as a
>>>way to encapsulate data (like a private member in C++ and
>>>a getter/setter, although there seems to be more to it.
>>>Closure are all the rage in the lisp-world (-:    )
>> 
>> 
>> You can think of a closure as a lightweight object with only a single
>> function to its name, and less to write. This ignores the fact that you
>> can define local functions inside the closure, and have said single
>> function call them based on its first argument; as a matter of fact,
>> the CLOS is (or could be, at least) entirely written in Lisp,
>> presumably with closures.
>> 
>> Take a look at this:
>> 
>> (let (slot-1 slot-2 slot-3
>>      (fn-1 (lambda (x) (+ slot-1 x)))
>>       ..etc..)
>>   (defun object-1 (function &rest args)
>>      (ecase function
>>         'fn-1 (apply fn-1 args)
>>         'fn-2 (apply fn-2 args))))
>> 
>> See what I mean?
>> Now, throw in a "defclass" macro, and...
> 
> You can certainly have more than one function closed over the same
> environment.

Well, yes. I was going for an "object" look, and missed it completely. Mea
culpa.
From: Svein Ove Aas
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <Ejwpc.1771$eH3.41979@news4.e.nsc.no>
Svein Ove Aas wrote:

> Take a look at this:
> 
> (let (slot-1 slot-2 slot-3
>      (fn-1 (lambda (x) (+ slot-1 x)))
>       ..etc..)
>   (defun object-1 (function &rest args)
>      (ecase function
>         'fn-1 (apply fn-1 args)
>         'fn-2 (apply fn-2 args))))
> 
> See what I mean?
> Now, throw in a "defclass" macro, and...

There is, of course, a major problem with the above "class": It can't be
instantiated, or anything nifty like that.

I'll leave fixing this as an exercise to the reader.
From: John Thingstad
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <opr72358r2pqzri1@mjolner.upc.no>
There is a equivalence between closures and the sington pattern.
(from "design patterns" Gamma et al)
Closures are more like ada's packages than classes.
That is it supports a modular programming abstraction.
It is often used when C++ would use templates too.
(Just to put closures in perspective)

On Sat, 15 May 2004 23:55:18 +0200, Svein Ove Aas  
<··············@brage.info> wrote:

> Svein Ove Aas wrote:
>
>> Take a look at this:
>>
>> (let (slot-1 slot-2 slot-3
>>      (fn-1 (lambda (x) (+ slot-1 x)))
>>       ..etc..)
>>   (defun object-1 (function &rest args)
>>      (ecase function
>>         'fn-1 (apply fn-1 args)
>>         'fn-2 (apply fn-2 args))))
>>
>> See what I mean?
>> Now, throw in a "defclass" macro, and...
>
> There is, of course, a major problem with the above "class": It can't be
> instantiated, or anything nifty like that.
>
> I'll leave fixing this as an exercise to the reader.



-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
From: David Steuber
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87y8nswqdc.fsf@david-steuber.com>
"John Thingstad" <··············@chello.no> writes:

> There is a equivalence between closures and the sington pattern.
> (from "design patterns" Gamma et al)

Are you sure?  The singleton pattern calls for precisely one instance
of a class.  It says nothing about a single interface.  Of course with
let you can close several functions over several variables, so I guess
you have a good point in that sense.  The only difference I would see
is that you don't get a type for generic functions to specialize on.
Maybe I'm being picky.

I have some code that sorta looks like this (not real lisp, but the
idea is there):

(let ((handlers (make-hashtable :test #'string-equal))
      (handlers-mutex (sb-thread:make-mutex)))
  (defun add-handler (string fn)
    (sb-thread:with-mutex (handlers-mutex)
      (setf (gethash string handlers) fn)))
  (defun get-handler (string) ...)
  (defun set-default-handler (fn) ...))

SBCL did print out that the functions were closures.

-- 
I wouldn't mind the rat race so much if it wasn't for all the damn cats.
From: Thomas Schilling
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gp1h9F53p2fU1@uni-berlin.de>
John Thingstad wrote:
> There is a equivalence between closures and the sington pattern.
> (from "design patterns" Gamma et al)

Er, really? Can't find the book right now. But IIRC a singleton is a class
that can have /only one instance/. A closure can have more than one
instance I think. (It only has just one, when you create it with defun.)
From: David Steuber
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87oeooqq77.fsf@david-steuber.com>
Thomas Schilling <······@yahoo.de> writes:

> John Thingstad wrote:
> > There is a equivalence between closures and the sington pattern.
> > (from "design patterns" Gamma et al)
> 
> Er, really? Can't find the book right now. But IIRC a singleton is a class
> that can have /only one instance/. A closure can have more than one
> instance I think. (It only has just one, when you create it with defun.)

I believe you are correct.

(defun make-closure (foo)
  (lambda () foo))

will return a new closure each time it is called.

-- 
I wouldn't mind the rat race so much if it wasn't for all the damn cats.
From: Gareth McCaughan
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87oeom16hj.fsf@g.mccaughan.ntlworld.com>
"John Thingstad" <··············@chello.no> writes:

> There is a equivalence between closures and the sington pattern.
> (from "design patterns" Gamma et al)

I agree with the two people who have already posted saying
"Um, if there is then I don't see it". Are you sure about
this?

> Closures are more like ada's packages than classes.
> That is it supports a modular programming abstraction.

I think that if you try to use closures as you would
use packages in Ada, and nowhere else, then you will
confuse the heck out of other people reading your code
and fail to get all the advantages that closures can
offer.

Packages are, at least conceptually, big heavyweight things
(I think this is true in every language that supports
anything called "packages"!), and you also want to be
able to use closures for little stuff like

    (defun smaller-elements (upper-bound list)
      (remove-if-not (lambda (x) (< x upper-bound)) list))

> It is often used when C++ would use templates too.
> (Just to put closures in perspective)

In so far as that's true, I think it would be better
stated as "C++, lacking closures, has to use templates
as a half-assed substitute for some uses of them". :-)
For example, something akin to the SMALLER-ELEMENTS
function above could be written as follows in C++.
I'll probably have some syntactic details wrong (e.g.,
because of the "typename"s  and "templates" you need
to insert to disambiguate things for the poor overloaded
parser).

    // B is something like vector<T> and C is T.
    // Destructively remove from |container| all
    // items not smaller than |item| according to
    // C::operator< .
    template <template <typename A> B, typename C>
    void smaller_elements(C item, B<C> container) {
      container.erase(remove_if(container.begin(), container.end(),
                                not1(bind2nd(less<C>(),item))));
    }

Good news: this works for any resizable STL container.
Bad news: it's as ugly as, um, something very ugly.
I think it's much more appropriate to describe all
the bind2nd business as a way to pretend you have
closures, than to describe closures as a way to pretend
you have bind2nd.

-- 
Gareth McCaughan
.sig under construc
From: Reinhard Gantar
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gpqsnF5bsnnU1@uni-berlin.de>
Svein Ove Aas wrote:
> Reinhard Gantar wrote:
> 
> 
>>Pascal Costanza wrote:
>>
>><SNIPPO />
>>
>>>
>>>You are missing that DEFUN is specified to create a function that is
>>>globally accessible via the name given in the DEFUN form. Things that
>>>are accessible via global names are not gc'ed. That's all.
>>>
>>
>>Yes, I should have thought of that (after all, closure-1 IS
>>visible outside the (let ...).
>>
>>
>>>If you want to build a function that can be immediately gc'ed, use
>>>LAMBDA to create an anonymous function:
>>>
>>>(let ((e 1))
>>>  #'(lambda () e))
>>>
>>
>>Makes sense.
>>
>>>However, in order to be able to use it, you have to assign it to a
>>>variable, and then you are circumventing garbage collection again, at
>>>least until the variable goes out of scope. However, that's what you
>>>want, right?
>>
>>This is what makes a closure tick. I understand it as a
>>way to encapsulate data (like a private member in C++ and
>>a getter/setter, although there seems to be more to it.
>>Closure are all the rage in the lisp-world (-:    )
> 
> 
> You can think of a closure as a lightweight object with only a single
> function to its name, and less to write. This ignores the fact that you
> can define local functions inside the closure, and have said single
> function call them based on its first argument; as a matter of fact, the
> CLOS is (or could be, at least) entirely written in Lisp, presumably with
> closures.
> 
> Take a look at this:
> 
> (let (slot-1 slot-2 slot-3
>      (fn-1 (lambda (x) (+ slot-1 x)))
>       ..etc..)
>   (defun object-1 (function &rest args)
>      (ecase function
>         'fn-1 (apply fn-1 args)
>         'fn-2 (apply fn-2 args))))
> 
> See what I mean?
> Now, throw in a "defclass" macro, and...

I see what you mean. This is where I want to get -- classes.
CLOS is a clever acronym in the light of that closures play
such an important role. On to classes, thanks...
From: Pascal Costanza
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <c868vv$6vd$1@newsreader2.netcologne.de>
Reinhard Gantar wrote:

> Pascal Costanza wrote:
[...]

>> P.S.: Common Lisp listeners bind the results of the previous three 
>> evaluated forms in the variables *, ** and *** respectively. So after 
>> typing in the above form, you can in fact say the following.
>>
>> (funcall *)
>>
>> ...which should yield 1.
> 
> Sounds perlish to me (:

...but only on a very superficial level. Those variables are only 
available in the listener, they are not part of the language proper. 
That would indeed be ugly. But in the listener, they are very handy. 
Just try it out.


Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Reinhard Gantar
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gpm06F5af06U1@uni-berlin.de>
Pascal Costanza wrote:
> 
> 
> Reinhard Gantar wrote:
> 
>> Pascal Costanza wrote:
> 
> [...]
> 
>>> P.S.: Common Lisp listeners bind the results of the previous three 
>>> evaluated forms in the variables *, ** and *** respectively. So after 
>>> typing in the above form, you can in fact say the following.
>>>
>>> (funcall *)
>>>
>>> ...which should yield 1.
>>
>>
>> Sounds perlish to me (:
> 
> 
> ...but only on a very superficial level. Those variables are only 
> available in the listener, they are not part of the language proper. 

So I can retract my comment.

> That would indeed be ugly. But in the listener, they are very handy. 
> Just try it out.

I did so already, but only in the REPL. This feature is
handy, but in the age of arrow-key-ed command line history
not as much as it used to be, mehtinks. Wished I had known
this when tinkering with MUlisp on CP/M. It did not have
a command line history (but then again, maybe it did not
have the * ** *** feature either.

Kind regards
Gantar


> 
> 
> Pascal
> 
From: André Thieme
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <c881da$aab$1@ulric.tng.de>
Pascal Costanza wrote:

> If you want to build a function that can be immediately gc'ed, use 
> LAMBDA to create an anonymous function:
> 
> (let ((e 1))
>   #'(lambda () e))

Why do you use the #' ? (btw, is this a special form?)

I get the same results, see:

CL-USER 3 > (let ((e 1))
               (lambda () e))
#<interpreted closure (LAMBDA NIL E)>

CL-USER 4 > (let ((e 1))
               #'(lambda () e))
#<interpreted closure (LAMBDA NIL E)>



Andr�
--
From: Edi Weitz
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <m3n048z8kq.fsf@ella.agharta.de>
On Sun, 16 May 2004 17:31:06 +0200, Andr� Thieme <······························@justmail.de> wrote:

> Pascal Costanza wrote:
>
>> If you want to build a function that can be immediately gc'ed, use
>> LAMBDA to create an anonymous function:
>> (let ((e 1))
>>   #'(lambda () e))
>
> Why do you use the #' ? (btw, is this a special form?)
>
> I get the same results, see:
>
> CL-USER 3 > (let ((e 1))
>                (lambda () e))
> #<interpreted closure (LAMBDA NIL E)>
>
> CL-USER 4 > (let ((e 1))
>                #'(lambda () e))
> #<interpreted closure (LAMBDA NIL E)>

The two forms are equivalent, it's just a style issue. LAMBDA is a
macro which will expand (LAMBDA () E) into #'(LAMBDA () E) which is
short for (FUNCTION (LAMBDA () E)). See the dictionary entry for
LAMBDA in the CLHS.

#' isn't a special operator, it's a reader macro which expands to
FUNCTION which /is/ a special operator.

Edi.
From: Nils Gösche
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <878yfsz8h3.fsf@darkstar.cartan.de>
Andr� Thieme   <······························@justmail.de> writes:

> Pascal Costanza wrote:
> 
> > If you want to build a function that can be immediately gc'ed, use
> > LAMBDA to create an anonymous function:
> > (let ((e 1))
> >   #'(lambda () e))
> 
> Why do you use the #' ? (btw, is this a special form?)
> 
> I get the same results, see:
> 
> CL-USER 3 > (let ((e 1))
>                (lambda () e))
> #<interpreted closure (LAMBDA NIL E)>
> 
> CL-USER 4 > (let ((e 1))
>                #'(lambda () e))
> #<interpreted closure (LAMBDA NIL E)>

It doesn't make any difference.  LAMBDA is a macro, and if you write
(lambda () e) without the #' in front of it, it expands to (FUNCTION
(LAMBDA () E)) at macro expansion time.  #' itself, OTOH, is a read
macro that will transform #'(lambda () e) into (FUNCTION (LAMBDA ()
E)) at read time already.

The LAMBDA macro is relatively new; earlier, you had to use the #' in
front of lambdas.

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x7E4651AD
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <wuNpc.64925$Fl5.367@okepread04>
Nils G�sche wrote:

> Andr� Thieme   <······························@justmail.de> writes:
> 
> 
>>Pascal Costanza wrote:
>>
>>
>>>If you want to build a function that can be immediately gc'ed, use
>>>LAMBDA to create an anonymous function:
>>>(let ((e 1))
>>>  #'(lambda () e))
>>
>>Why do you use the #' ? (btw, is this a special form?)
>>
>>I get the same results, see:
>>
>>CL-USER 3 > (let ((e 1))
>>               (lambda () e))
>>#<interpreted closure (LAMBDA NIL E)>
>>
>>CL-USER 4 > (let ((e 1))
>>               #'(lambda () e))
>>#<interpreted closure (LAMBDA NIL E)>
> 
> 
> It doesn't make any difference.  LAMBDA is a macro, and if you write
> (lambda () e) without the #' in front of it, it expands to (FUNCTION
> (LAMBDA () E)) at macro expansion time.  #' itself, OTOH, is a read
> macro that will transform #'(lambda () e) into (FUNCTION (LAMBDA ()
> E)) at read time already.
> 
> The LAMBDA macro is relatively new; earlier, you had to use the #' in
> front of lambdas.

So FUNCTION is where all the power is regarding turning a LAMBDA form 
into a closure?  I had wondered the story behind #'(lambda ...).  Why 
wasn't LAMBDA implemented as a special form of its own in the first place?
From: Nils Gösche
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87zn88xl2s.fsf@darkstar.cartan.de>
Ari Johnson <·····@hotmail.com> writes:

> Nils G�sche wrote:
> 
> > Andr� Thieme   <······························@justmail.de> writes:
> >
> >>I get the same results, see:
> >>
> >>CL-USER 3 > (let ((e 1))
> >>               (lambda () e))
> >>#<interpreted closure (LAMBDA NIL E)>
> >>
> >>CL-USER 4 > (let ((e 1))
> >>               #'(lambda () e))
> >>#<interpreted closure (LAMBDA NIL E)>

> > It doesn't make any difference.  LAMBDA is a macro, and if you
> > write (lambda () e) without the #' in front of it, it expands to
> > (FUNCTION (LAMBDA () E)) at macro expansion time.  #' itself,
> > OTOH, is a read macro that will transform #'(lambda () e) into
> > (FUNCTION (LAMBDA () E)) at read time already.

> > The LAMBDA macro is relatively new; earlier, you had to use the #'
> > in front of lambdas.
> 
> So FUNCTION is where all the power is regarding turning a LAMBDA
> form into a closure?  I had wondered the story behind #'(lambda
> ...).  Why wasn't LAMBDA implemented as a special form of its own in
> the first place?

People who know this better than I have posted about this before (it
should be possible to find it with google).  It's just that a (lambda
...) form can often be used in places where you'd normally use a
function name.  Compare

(let ((e 1))
  (flet ((foo ()
           e))
    #'foo))

with

(let ((e 1))
  #'(lambda ()
      e))

for instance.  Or

(flet ((foo (x)
         (+ x 42)))
  (foo 17))

with

((lambda (x) (+ x 42)) 17).

It's more like a lambda expression (lambda ...) is taking the place of
a function name here.

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x7E4651AD
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <pYOpc.65788$Fl5.19198@okepread04>
Nils G�sche wrote:
> Ari Johnson <·····@hotmail.com> writes:
>>So FUNCTION is where all the power is regarding turning a LAMBDA
>>form into a closure?  I had wondered the story behind #'(lambda
>>...).  Why wasn't LAMBDA implemented as a special form of its own in
>>the first place?
> 
> People who know this better than I have posted about this before (it
> should be possible to find it with google).  It's just that a (lambda
> ...) form can often be used in places where you'd normally use a
> function name.  Compare

Here's where I have some remaining confusion.  (lambda ...) is a macro 
that expands to (function (lambda ...)), right?

* ((lambda ()))
NIL
* (#'(lambda ()))
Error: #'(LAMBDA ()) is not a function name
* ((function (lambda ())))
Error: #'(LAMBDA ()) is not a function name
* (#'car (cons 1 2))
Error: #'CAR is not a function name

There's a discrepancy here, but I haven't read the entire spec or 
anything, so I may be missing a subtle rule.  Can anyone cite it?
From: Pascal Costanza
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <c88ft6$nh6$1@newsreader2.netcologne.de>
Ari Johnson wrote:

> Here's where I have some remaining confusion.  (lambda ...) is a macro 
> that expands to (function (lambda ...)), right?
> 
> * ((lambda ()))
> NIL
> * (#'(lambda ()))
> Error: #'(LAMBDA ()) is not a function name
> * ((function (lambda ())))
> Error: #'(LAMBDA ()) is not a function name
> * (#'car (cons 1 2))
> Error: #'CAR is not a function name
> 
> There's a discrepancy here, but I haven't read the entire spec or 
> anything, so I may be missing a subtle rule.  Can anyone cite it?

You are just rediscovering the fact that the car position and the cdr 
positions in forms are treated differently. For example, in (f f) the 
first f refers to a function while the second f refers to a value. 
That's why it's called a Lisp-2.

The form ((lambda () 5)) has the lambda expression in its car position, 
and there it is treated specially. The form (funcall (lambda () 5)) has 
the lambda expression in a cdr position, and there it just refers to a 
macro.

Compare this with (f #'f)

You can find more details in 3.1.2.1.2 in the HyperSpec.


Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <rfQpc.164759$f_5.63538@lakeread01>
Pascal Costanza wrote:

> 
> Ari Johnson wrote:
> 
>> Here's where I have some remaining confusion.  (lambda ...) is a macro 
>> that expands to (function (lambda ...)), right?
>>
>> * ((lambda ()))
>> NIL
>> * (#'(lambda ()))
>> Error: #'(LAMBDA ()) is not a function name
>> * ((function (lambda ())))
>> Error: #'(LAMBDA ()) is not a function name
>> * (#'car (cons 1 2))
>> Error: #'CAR is not a function name
>>
>> There's a discrepancy here, but I haven't read the entire spec or 
>> anything, so I may be missing a subtle rule.  Can anyone cite it?
> 
> 
> You are just rediscovering the fact that the car position and the cdr 
> positions in forms are treated differently. For example, in (f f) the 
> first f refers to a function while the second f refers to a value. 
> That's why it's called a Lisp-2.
> 
> The form ((lambda () 5)) has the lambda expression in its car position, 
> and there it is treated specially. The form (funcall (lambda () 5)) has 
> the lambda expression in a cdr position, and there it just refers to a 
> macro.
> 
> Compare this with (f #'f)
> 
> You can find more details in 3.1.2.1.2 in the HyperSpec.

That's exactly what I was looking for: a citation; thanks.
From: Nils Gösche
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87r7tkxik3.fsf@darkstar.cartan.de>
Ari Johnson <·····@hotmail.com> writes:

> Nils G�sche wrote:
> > Ari Johnson <·····@hotmail.com> writes:
> >>So FUNCTION is where all the power is regarding turning a LAMBDA
> >>form into a closure?  I had wondered the story behind #'(lambda
> >>...).  Why wasn't LAMBDA implemented as a special form of its own in
> >>the first place?
> > People who know this better than I have posted about this before (it
> > should be possible to find it with google).  It's just that a (lambda
> > ...) form can often be used in places where you'd normally use a
> > function name.  Compare
> 
> Here's where I have some remaining confusion.  (lambda ...) is a
> macro that expands to (function (lambda ...)), right?

Yes.

> * ((lambda ()))
> NIL
> * (#'(lambda ()))
> Error: #'(LAMBDA ()) is not a function name
> * ((function (lambda ())))
> Error: #'(LAMBDA ()) is not a function name
> * (#'car (cons 1 2))
> Error: #'CAR is not a function name
> 
> There's a discrepancy here, but I haven't read the entire spec or
> anything, so I may be missing a subtle rule.  Can anyone cite it?

Well, you have to follow the rules for evaluating forms.  See "3.1.2
The Evaluation Model" in the HyperSpec.  The first element of a cons
form should be a symbol, naming a special form, macro or function,
/or/ it could be a list, but then it must be a lambda expression;
something like ((function car) ...) is simply not allowed.  In the
latter case, ((lambda ...) ...), the LAMBDA expression is /not/
macroexpanded anymore, just as it isn't when it appears inside a
FUNCTION form.  Is it used "as is".

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x7E4651AD
From: Svein Ove Aas
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <8WLpc.1919$eH3.43861@news4.e.nsc.no>
Andr� Thieme wrote:

> Pascal Costanza wrote:
> 
>> If you want to build a function that can be immediately gc'ed, use
>> LAMBDA to create an anonymous function:
>> 
>> (let ((e 1))
>>   #'(lambda () e))
> 
> Why do you use the #' ? (btw, is this a special form?)

It used to be required. Many people (me amongst them, until recently)
don't know better.

And, no, it's a reader macro; it expands to (function ...), which *is* a
special form.
From: Barry Margolin
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <barmar-60C7EF.11584916052004@comcast.dca.giganews.com>
In article <····················@news4.e.nsc.no>,
 Svein Ove Aas <··············@brage.info> wrote:

> Andr� Thieme wrote:
> 
> > Pascal Costanza wrote:
> > 
> >> If you want to build a function that can be immediately gc'ed, use
> >> LAMBDA to create an anonymous function:
> >> 
> >> (let ((e 1))
> >>   #'(lambda () e))
> > 
> > Why do you use the #' ? (btw, is this a special form?)
> 
> It used to be required. Many people (me amongst them, until recently)
> don't know better.

Or old habits die hard.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Pascal Costanza
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <c882uc$qke$2@newsreader2.netcologne.de>
Svein Ove Aas wrote:

>>Why do you use the #' ? (btw, is this a special form?)
> 
> It used to be required. Many people (me amongst them, until recently)
> don't know better.

Some who do know better still prefer the #' because they think it is 
better to be explicit. I am not among them, but I think it's a good reason.


Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Pascal Costanza
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <c882qb$qke$1@newsreader2.netcologne.de>
Andr� Thieme wrote:

> Pascal Costanza wrote:
> 
>> If you want to build a function that can be immediately gc'ed, use 
>> LAMBDA to create an anonymous function:
>>
>> (let ((e 1))
>>   #'(lambda () e))

> Why do you use the #' ? (btw, is this a special form?)
> 
> I get the same results, see:
> 
> CL-USER 3 > (let ((e 1))
>               (lambda () e))
> #<interpreted closure (LAMBDA NIL E)>
> 
> CL-USER 4 > (let ((e 1))
>               #'(lambda () e))
> #<interpreted closure (LAMBDA NIL E)>

I usually don't use the sharp-quote for lambda, but in this case I think 
it was clearer to do so explicitly.

Common Lisp distinguishes functions and values, and sharp-quote is used 
to indicate that you are dealing with a function. The expression

(lambda (...) ...)

is just a macro that expands into the equivalent

#'(lambda (...) ...)

It's only provided by Common Lisp for convenience.


Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Svein Ove Aas
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <icupc.1724$eH3.42004@news4.e.nsc.no>
Reinhard Gantar wrote:

> Dear c.l.l.,
> 
> in David Lamkins "Successful Lisp", Chapter 11,
> we read a simple example for closures:
> 
> ? (let ((e 1))
>      (defun closure-1 () e))
> 
> ? (closure-1)
> gives 1.
> 
> I know what this does (creating a cons for storing 1,
> binding it to e). The variable e is not visible because
> the scope is limited to the inside of the let.
> So far so good. What I don't understand is why this
> example works. According to what I understood, the
> cons with the 1, together with its binding e should
> be gc-ed as soon as closure-1 is gc-ed, and
> closure-1 should be gc-ed as soon as the let-form is
> gc-ed. Since the let-form has been created by the
> REPL, it should be gc-ed IMMEDIATELY, just as a
> form like
> 
> ? (+ 42 23)
> -> 67
> 
> is gc-ed right after the evaluation (or is it?)
> What's wrong here? Is it a mere convention that
> (let ...)s or (defun ...)s don't get gc-ed right away? This
> cruft does not look like my dear lisp at all.
> I guess I'm missing something, but what?

(defun), being a macro[1], does quite a bit of work in the background; if
it weren't in fact a special form it would evaluate to something like
this:

(macroexpand '(let ((e 1)) (defun closure-1 () e))
=> (setf (symbol-function 'closure-1)
        (let ((e 1)) (lambda () e)))


The setf above is the point; it puts a reference to closure-1 in the
global function table; said table, being global, is immune to the GC, and
since it has a reference to closure-1, so is that.

Most implementations do this more efficiently, but the above code does
work.

1: All right, it may be a special form. The effect is the same.
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <YHupc.161706$f_5.50702@lakeread01>
Svein Ove Aas wrote:
> Reinhard Gantar wrote:
> 
> 
>>Dear c.l.l.,
>>
>>in David Lamkins "Successful Lisp", Chapter 11,
>>we read a simple example for closures:
>>
>>? (let ((e 1))
>>     (defun closure-1 () e))
>>
>>? (closure-1)
>>gives 1.
>>
>>I know what this does (creating a cons for storing 1,
>>binding it to e). The variable e is not visible because
>>the scope is limited to the inside of the let.
>>So far so good. What I don't understand is why this
>>example works. According to what I understood, the
>>cons with the 1, together with its binding e should
>>be gc-ed as soon as closure-1 is gc-ed, and
>>closure-1 should be gc-ed as soon as the let-form is
>>gc-ed. Since the let-form has been created by the
>>REPL, it should be gc-ed IMMEDIATELY, just as a
>>form like
>>
>>? (+ 42 23)
>>-> 67
>>
>>is gc-ed right after the evaluation (or is it?)
>>What's wrong here? Is it a mere convention that
>>(let ...)s or (defun ...)s don't get gc-ed right away? This
>>cruft does not look like my dear lisp at all.
>>I guess I'm missing something, but what?
> 
> 
> (defun), being a macro[1], does quite a bit of work in the background; if
> it weren't in fact a special form it would evaluate to something like
> this:
> 
> (macroexpand '(let ((e 1)) (defun closure-1 () e))
> => (setf (symbol-function 'closure-1)
>         (let ((e 1)) (lambda () e)))

FWIW, CLISP gives me
   (symbol-function 'defun) => #<MACRO #<COPMILED-CLOSURE DEFUN>>
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <0Kupc.161713$f_5.126008@lakeread01>
Ari Johnson wrote:
<typo-correction note="Why didn't I just copy and paste?">
> FWIW, CLISP gives me
>   (symbol-function 'defun) => #<MACRO #<COMPILED-CLOSURE DEFUN>>
</typo-correction>
From: Reinhard Gantar
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gneitF4s29mU1@uni-berlin.de>
Ari Johnson wrote:
> Ari Johnson wrote:
> <typo-correction note="Why didn't I just copy and paste?">
> 
>> FWIW, CLISP gives me
>>   (symbol-function 'defun) => #<MACRO #<COMPILED-CLOSURE DEFUN>>
> 
> </typo-correction>

This is rad because I thought that "closure" is a term that
refers to a concept, not a particular syntactic (as far as "syntax"
goes in lisp) construct. In other words, I thought a closure
is something like building a factory in C++. You and your fellows
know that it is a factory because it works like one, but the
compiler, seeing only classes and method-calls does not.

So lisp DOES know a closure when it sees one. How's that?
Are all defuns that are not at the top-level (lexically)
closures? /Successful Lisp/ does not mention this.

Thanks
Gantar
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <tAvpc.161888$f_5.107931@lakeread01>
Reinhard Gantar wrote:

> Ari Johnson wrote:
> 
>> Ari Johnson wrote:
>> <typo-correction note="Why didn't I just copy and paste?">
>>
>>> FWIW, CLISP gives me
>>>   (symbol-function 'defun) => #<MACRO #<COMPILED-CLOSURE DEFUN>>
>>
>>
>> </typo-correction>
> 
> 
> This is rad because I thought that "closure" is a term that
> refers to a concept, not a particular syntactic (as far as "syntax"
> goes in lisp) construct. In other words, I thought a closure
> is something like building a factory in C++. You and your fellows
> know that it is a factory because it works like one, but the
> compiler, seeing only classes and method-calls does not.
> 
> So lisp DOES know a closure when it sees one. How's that?
> Are all defuns that are not at the top-level (lexically)
> closures? /Successful Lisp/ does not mention this.

Yep.  In fact, all functions can be considered closures, it's just that 
those created at the top level are created in the null lexical 
environment, meaning they close over an empty set of bindings.
From: Thomas Schilling
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gnq7pF4sbj5U1@uni-berlin.de>
Reinhard Gantar wrote:
> This is rad because I thought that "closure" is a term that
> refers to a concept, not a particular syntactic (as far as "syntax"
> goes in lisp) construct. In other words, I thought a closure
> is something like building a factory in C++. You and your fellows
> know that it is a factory because it works like one, but the
> compiler, seeing only classes and method-calls does not.
> 
> So lisp DOES know a closure when it sees one. How's that?
> Are all defuns that are not at the top-level (lexically)
> closures? /Successful Lisp/ does not mention this.

As others said, think of a closure as a lighweight object. If you return a
function as a result of another function e.g.:

  (defun is (x)
    "Return a function that returns t if its param is EQL to X."
    #'(lambda (y) (eql y x)))

it saves the values of all the accessed external (lexical[�]) variables
within the function object.

In C++ (or Python) you could create a class and overload the function
application operator and the function IS would then return an object of
that class with the external variables initialized properly.

Of course this would be /much/ overhead (and I'm even not sure whether this
would even work).

If the function doesn't access external lexical variables you could put it
into a class with no member fields, so, yes, this would be an empty lexical
closure. (of course this can then be optimized.)

I think this model works quite well, except, that it doesn't describe how
several functions can close over one variable ...

[�] For special variables this doesn't work.

Any corrections?
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <mUypc.62982$Fl5.60345@okepread04>
Thomas Schilling wrote:

> Reinhard Gantar wrote:
> 
>>This is rad because I thought that "closure" is a term that
>>refers to a concept, not a particular syntactic (as far as "syntax"
>>goes in lisp) construct. In other words, I thought a closure
>>is something like building a factory in C++. You and your fellows
>>know that it is a factory because it works like one, but the
>>compiler, seeing only classes and method-calls does not.
>>
>>So lisp DOES know a closure when it sees one. How's that?
>>Are all defuns that are not at the top-level (lexically)
>>closures? /Successful Lisp/ does not mention this.
> 
> 
> As others said, think of a closure as a lighweight object. If you return a
> function as a result of another function e.g.:
> 
>   (defun is (x)
>     "Return a function that returns t if its param is EQL to X."
>     #'(lambda (y) (eql y x)))
> 
> it saves the values of all the accessed external (lexical[�]) variables
> within the function object.
> 
> In C++ (or Python) you could create a class and overload the function
> application operator and the function IS would then return an object of
> that class with the external variables initialized properly.
> 
> Of course this would be /much/ overhead (and I'm even not sure whether this
> would even work).

How do you figure there'd be "/much/" overhead?  Closures in C++ may be 
more work to set up than in Lisp, but why would the C++ implementation 
incur any heavier overhead than the Lisp implementation would?
From: Thomas Schilling
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gntmlF4p6knU1@uni-berlin.de>
Ari Johnson wrote:

>> Of course this would be /much/ overhead (and I'm even not sure whether
>> this would even work).
> 
> How do you figure there'd be "/much/" overhead?  Closures in C++ may be
> more work to set up than in Lisp, but why would the C++ implementation
> incur any heavier overhead than the Lisp implementation would?

I mean typing overhead ;)

My shortest version would be:

template <T>
class is_closure {
   T _x;
  public:
   is_closure(T x) : _x(x) {}
   bool operator () (T y) {
     return y == _x;
   }
};

template <T> is_closure<T> is(T x) {
  return is_closure(x);
}

Hm, I prefer the Lisp version ...
From: Alexey Dejneka
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <m31xllf4pa.fsf@comail.ru>
Thomas Schilling <······@yahoo.de> writes:

> As others said, think of a closure as a lighweight object. If you return a
> function as a result of another function e.g.:
> 
>   (defun is (x)
>     "Return a function that returns t if its param is EQL to X."
>     #'(lambda (y) (eql y x)))
> 
> it saves the values of all the accessed external (lexical[�]) variables
> within the function object.

While in such case an implementation may do it as an optimization, in
general, it saves variables, not their values:

  CL-USER> (defun make-counter ()
             (let ((count 0))
               (list (lambda () count)
                     (lambda () (incf count)))))
  MAKE-COUNTER
  CL-USER> (defvar *c1* (make-counter))
  *C1*
  CL-USER> (defvar *c2* (make-counter))
  *C2*
  CL-USER> (funcall (second *c1*))
  1
  CL-USER> (funcall (second *c1*))
  2
  CL-USER> (funcall (second *c1*))
  3
  CL-USER> (funcall (second *c2*))
  1
  CL-USER> (funcall (first *c1*))
  3
  CL-USER> (funcall (first *c2*))
  1

Here LET creates a new variable, which is accessed by both reader and
writer methods.

-- 
Regards,
Alexey Dejneka

"Alas, the spheres of truth are less transparent than those of
illusion." -- L.E.J. Brouwer
From: Thomas Schilling
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gp1pgF53p2fU2@uni-berlin.de>
Alexey Dejneka wrote:
> Thomas Schilling <······@yahoo.de> writes:
>> it saves the values of all the accessed external (lexical[�]) variables
>> within the function object.
> 
> While in such case an implementation may do it as an optimization, in
> general, it saves variables, not their values:

Er, yes. Right.
From: Reinhard Gantar
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gnapuF4q6a7U1@uni-berlin.de>
Svein Ove Aas wrote:
<SNIPPO />

> 
> (defun), being a macro[1], does quite a bit of work in the background; if
> it weren't in fact a special form it would evaluate to something like
> this:
> 
> (macroexpand '(let ((e 1)) (defun closure-1 () e))
> => (setf (symbol-function 'closure-1)
>         (let ((e 1)) (lambda () e)))
> 
> 

This is more than I can digest at this point, but I can't
wait to look up "symbol-function" (no spoilers, please).



> The setf above is the point; it puts a reference to closure-1 in the
> global function table; said table, being global, is immune to the GC, and
> since it has a reference to closure-1, so is that.

Tip of the hat
Gantar



> 
> Most implementations do this more efficiently, but the above code does
> work.
> 
> 1: All right, it may be a special form. The effect is the same.
From: Gareth McCaughan
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87brkp4cc2.fsf@g.mccaughan.ntlworld.com>
Reinhard Gantar wrote:

> ? (let ((e 1))
>      (defun closure-1 () e))
> 
> ? (closure-1)
> gives 1.
> 
> I know what this does (creating a cons for storing 1,
> binding it to e).

Some other people have addressed your actual question,
so I thought I'd jump in with a bit of mindless pedantry.
There's no reason why the thing 1 is stored in should
be a cons cell. In fact, the compiler is entirely at
liberty to notice that there's no way for E to get its
binding changed, and therefore to optimize CLOSURE-1
so it just always returns 1. Indeed, CMUCL produces
the exact same code for the definition above as for

    (defun closure-1 () 1)

-- 
Gareth McCaughan
.sig under construc
From: Reinhard Gantar
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <2gni9fF4rkbsU1@uni-berlin.de>
Gareth McCaughan wrote:
> Reinhard Gantar wrote:
> 
> 
>>? (let ((e 1))
>>     (defun closure-1 () e))
>>
>>? (closure-1)
>>gives 1.
>>
>>I know what this does (creating a cons for storing 1,
>>binding it to e).
> 
> 
> Some other people have addressed your actual question,
> so I thought I'd jump in with a bit of mindless pedantry.

Well, I guess pedantry is required for lisp.

> There's no reason why the thing 1 is stored in should
> be a cons cell. In fact, the compiler is entirely at
> liberty to notice that there's no way for E to get its
> binding changed, and therefore to optimize CLOSURE-1
> so it just always returns 1. Indeed, CMUCL produces
> the exact same code for the definition above as for
> 
>     (defun closure-1 () 1)

Nice compiler, yes. Things like this are probably why CMUCL has such
a good reputation.




> 
From: Joe Marshall
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <smdz6ug3.fsf@ccs.neu.edu>
Reinhard Gantar <········@uptime.at> writes:

> Gareth McCaughan wrote:
>> Reinhard Gantar wrote:
>>
>>>? (let ((e 1))
>>>     (defun closure-1 () e))
>>>
>>>? (closure-1)
>>>gives 1.
>>>
>>>I know what this does (creating a cons for storing 1,
>>>binding it to e).
>> Some other people have addressed your actual question,
>> so I thought I'd jump in with a bit of mindless pedantry.
>
> Well, I guess pedantry is required for lisp.

Sophistry is important, too.
From: André Thieme
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <c8821g$b34$1@ulric.tng.de>
Gareth McCaughan wrote:

> Some other people have addressed your actual question,
> so I thought I'd jump in with a bit of mindless pedantry.
> There's no reason why the thing 1 is stored in should
> be a cons cell. In fact, the compiler is entirely at
> liberty to notice that there's no way for E to get its
> binding changed, and therefore to optimize CLOSURE-1
> so it just always returns 1. Indeed, CMUCL produces
> the exact same code for the definition above as for
> 
>     (defun closure-1 () 1)

Lispworks also produces the same code for both versions.

(let ((e 1))
   (defun closure-1 () e))

(defun closure-2 () 1)


Anyway, I have a question:
I was only able to get the disassembled code for closure-1 when I put it 
in a separate file and compiled and loaded it. After that I could 
disassemble it. But when I am on the repl I can only
(disassemble 'closure-2)

What is they way to (disassemble 'closure-1) after typing defintions 
like above?


Andr�
--
From: Barry Margolin
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <barmar-EB7018.12095316052004@comcast.dca.giganews.com>
In article <············@ulric.tng.de>,
 Andr� Thieme <······························@justmail.de> wrote:

> Gareth McCaughan wrote:
> 
> > Some other people have addressed your actual question,
> > so I thought I'd jump in with a bit of mindless pedantry.
> > There's no reason why the thing 1 is stored in should
> > be a cons cell. In fact, the compiler is entirely at
> > liberty to notice that there's no way for E to get its
> > binding changed, and therefore to optimize CLOSURE-1
> > so it just always returns 1. Indeed, CMUCL produces
> > the exact same code for the definition above as for
> > 
> >     (defun closure-1 () 1)
> 
> Lispworks also produces the same code for both versions.
> 
> (let ((e 1))
>    (defun closure-1 () e))
> 
> (defun closure-2 () 1)
> 
> 
> Anyway, I have a question:
> I was only able to get the disassembled code for closure-1 when I put it 
> in a separate file and compiled and loaded it. After that I could 
> disassemble it. But when I am on the repl I can only
> (disassemble 'closure-2)
> 
> What is they way to (disassemble 'closure-1) after typing defintions 
> like above?

Did you try (compile 'closure-1) first?  The REPL might not 
automatically compile nested function definitions.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Nils Gösche
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <874qqgz6sj.fsf@darkstar.cartan.de>
Barry Margolin <······@alum.mit.edu> writes:

> In article <············@ulric.tng.de>,
>  Andr� Thieme <······························@justmail.de> wrote:

> > What is they way to (disassemble 'closure-1) after typing
> > defintions like above?
> 
> Did you try (compile 'closure-1) first?  The REPL might not
> automatically compile nested function definitions.

For some reason, LispWorks doesn't want to COMPILE interpreted
closures.  Apparently, it doesn't have to, either:

# The consequences are undefined if the lexical environment
# surrounding the function to be compiled contains any bindings other
# than those for macros, symbol macros, or declarations.

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x7E4651AD
From: Rob Warnock
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <AfmdnZ2oGb55hTXd4p2dnA@speakeasy.net>
Nils G�sche <···@cartan.de> wrote:
+---------------
| Barry Margolin <······@alum.mit.edu> writes:
| > Did you try (compile 'closure-1) first?  The REPL might not
| > automatically compile nested function definitions.
| 
| For some reason, LispWorks doesn't want to COMPILE interpreted
| closures.  Apparently, it doesn't have to, either:
| 
| # The consequences are undefined if the lexical environment
| # surrounding the function to be compiled contains any bindings other
| # than those for macros, symbol macros, or declarations.
+---------------

True, but you can fake that out if you really, really want a "naked"
compiled closure: simply wrap the lexical environment setting and
the target closure inside *another* closure, compile *that*, and
then FUNCALL it:

    > (funcall
	(compile nil
		 (lambda()
		   (let ((x 1))
		     (lambda () (incf x))))))
    ; Compiling LAMBDA NIL: 
    ; Compiling Top-Level Form: 

    #<Closure Over Function "LAMBDA NIL" {484EEE91}>
    > (funcall *)

    2
    > (funcall **)

    3
    > (funcall ***)

    4
    >


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Gareth McCaughan
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87smdy17co.fsf@g.mccaughan.ntlworld.com>
Nils G�sche wrote:

> For some reason, LispWorks doesn't want to COMPILE interpreted
> closures.  Apparently, it doesn't have to, either:
> 
> # The consequences are undefined if the lexical environment
> # surrounding the function to be compiled contains any bindings other
> # than those for macros, symbol macros, or declarations.

There's a good reason for that. The way you want to represent
the environment for an interpreted closure might be very
different from the way you want to represent it for a compiled
closure. And the thing you're trying to compile might be just
one of multiple closures sharing a lexical environment. Big
can of worms.

-- 
Gareth McCaughan
.sig under construc
From: Jeff Dalton
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <fx4pt936eks.fsf@todday.inf.ed.ac.uk>
Reinhard Gantar <········@uptime.at> writes:

> Since the let-form has been created by the
> REPL, it should be gc-ed IMMEDIATELY, just as a
> form like
> 
> ? (+ 42 23)
> -> 67
> 
> is gc-ed right after the evaluation (or is it?)

I've looked through this thread, and I don't think anyone
has addressed this point.

Why would anyone expect immediate garbage collection?

Is that how things work, somewhere, these days?

-- jd
From: Alan Shutko
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <878yfrokll.fsf@wesley.springies.com>
Jeff Dalton <····@todday.inf.ed.ac.uk> writes:

> Why would anyone expect immediate garbage collection?

Perl and Python are both refcounted so things are immediately
reclaimed.

-- 
Alan Shutko <···@acm.org> - I am the rocks.
From: Jeff Dalton
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <fx4n046aomn.fsf@todday.inf.ed.ac.uk>
Alan Shutko <···@acm.org> writes:

> Jeff Dalton <····@todday.inf.ed.ac.uk> writes:
> 
> > Why would anyone expect immediate garbage collection?
> 
> Perl and Python are both refcounted so things are immediately
> reclaimed.

Ok, so the way some people think of GC has been contaminated
by popular systems that use reference-counting.  (Sigh).

-- jd
From: Alan Shutko
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <878yfq3ncr.fsf@wesley.springies.com>
Jeff Dalton <····@todday.inf.ed.ac.uk> writes:

> Ok, so the way some people think of GC has been contaminated
> by popular systems that use reference-counting.  (Sigh).

Yep.  That's why people keep asking to put things on finalizers and
get upset when they don't run all the way... just ask the Java people.
CL isn't the only language which has to deal with these
misunderstandings.

-- 
Alan Shutko <···@acm.org> - I am the rocks.
From: Svein Ove Aas
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <07aqc.2097$RL3.55810@news2.e.nsc.no>
Alan Shutko wrote:

> Jeff Dalton <····@todday.inf.ed.ac.uk> writes:
> 
>> Why would anyone expect immediate garbage collection?
> 
> Perl and Python are both refcounted so things are immediately
> reclaimed.
> 
Wait, doesn't refcounting have a bit of trouble with circular structures?
How do they deal with that?
From: Alan Shutko
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87zn86207a.fsf@wesley.springies.com>
Svein Ove Aas <··············@brage.info> writes:

> Wait, doesn't refcounting have a bit of trouble with circular structures?
> How do they deal with that?

Not sure how Perl does it.  Python has a real gc which runs
periodically to resolve circular structures.

Hmm... some googling says that in perl, you just aren't supposed to
make circular structures, and if you need to, you should use weak
references.  (It does have a mark-and-sweep, but only runs it when
the interpreter is exiting.)

-- 
Alan Shutko <···@acm.org> - I am the rocks.
"On this side, memories are all we really own." Hercules
From: Barry Margolin
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <barmar-99CE47.18364817052004@comcast.dca.giganews.com>
In article <··············@wesley.springies.com>,
 Alan Shutko <···@acm.org> wrote:

> Svein Ove Aas <··············@brage.info> writes:
> 
> > Wait, doesn't refcounting have a bit of trouble with circular structures?
> > How do they deal with that?
> 
> Not sure how Perl does it.  Python has a real gc which runs
> periodically to resolve circular structures.
> 
> Hmm... some googling says that in perl, you just aren't supposed to
> make circular structures, and if you need to, you should use weak
> references.  (It does have a mark-and-sweep, but only runs it when
> the interpreter is exiting.)

Perl's references are sufficiently painful to use that I doubt they're 
frequently used for the kinds of complex structures that are likely to 
produce circularities.  So the problem simply doesn't arise often enough 
to warrant a strong solution.

I don't get the point of running a GC when the interpreter is exiting.  
Isn't *all* of its memory going to be reclaimed by the system when the 
process exits?

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Brian Downing
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <F3cqc.68991$536.11171903@attbi_s03>
In article <····························@comcast.dca.giganews.com>,
Barry Margolin  <······@alum.mit.edu> wrote:
> Perl's references are sufficiently painful to use that I doubt they're 
> frequently used for the kinds of complex structures that are likely to 
> produce circularities.  So the problem simply doesn't arise often enough 
> to warrant a strong solution.
> 
> I don't get the point of running a GC when the interpreter is exiting.  
> Isn't *all* of its memory going to be reclaimed by the system when the 
> process exits?

Presumably so the leftover perl objects don't leak into the program that the
perl interpreter is embedded into.

-bcd
-- 
*** Brian Downing <bdowning at lavos dot net> 
From: Daniel Barlow
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87ekpi4nob.fsf@noetbook.telent.net>
Barry Margolin <······@alum.mit.edu> writes:

> Perl's references are sufficiently painful to use that I doubt they're 
> frequently used for the kinds of complex structures that are likely to 
> produce circularities.  So the problem simply doesn't arise often enough 
> to warrant a strong solution.

A doubly-linked list is a complex structure?  A tree with parent
pointers in each node is complex?

Perl's OO system is mostly based on object references.  I found (and
continue to find) the subuseful GC a pain to deal with.


-dan

-- 
"please make sure that the person is your friend before you confirm"
From: Svein Ove Aas
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <37bqc.2115$RL3.55888@news2.e.nsc.no>
Alan Shutko wrote:

> Hmm... some googling says that in perl, you just aren't supposed to
> make circular structures, and if you need to, you should use weak
> references.  (It does have a mark-and-sweep, but only runs it when
> the interpreter is exiting.)

Huh?
What's the *point*? Surely *everything* is garbage at that point.
From: Alan Shutko
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <87fz9y1x8i.fsf@wesley.springies.com>
Svein Ove Aas <··············@brage.info> writes:

> Alan Shutko wrote:
>> ([Perl] does have a mark-and-sweep, but only runs it when
>> the interpreter is exiting.)
>
> Huh?  What's the *point*? Surely *everything* is garbage at that
> point.

Because Perl lets you put code into destructors, and people in
refcounted languages (and C++) like to put resource aquisition and
release code in constructors and destructors.  The gc on termination
will thus close network connections cleanly, release semaphores, and
all the other stuff people like to put in there.

-- 
Alan Shutko <···@acm.org> - I am the rocks.
New wizard spell: Fudge DM Rolls
From: Ari Johnson
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <A3eqc.69222$Fl5.14394@okepread04>
Svein Ove Aas wrote:

> Alan Shutko wrote:
> 
> 
>>Hmm... some googling says that in perl, you just aren't supposed to
>>make circular structures, and if you need to, you should use weak
>>references.  (It does have a mark-and-sweep, but only runs it when
>>the interpreter is exiting.)
> 
> 
> Huh?
> What's the *point*? Surely *everything* is garbage at that point.

If it's in Perl, it was probably garbage to start with. ;)
From: Marco Antoniotti
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <yAoqc.18$cn3.8701@typhoon.nyu.edu>
Alan Shutko wrote:

> Svein Ove Aas <··············@brage.info> writes:
> 
> 
>>Wait, doesn't refcounting have a bit of trouble with circular structures?
>>How do they deal with that?
> 
> 
> Not sure how Perl does it.  Python has a real gc which runs
> periodically to resolve circular structures.

So, they figured out that refcounting is lacking and they added a "real 
gc" to "fix" this problem.  Does it mean that now you have two GCs in 
Python?

Cheers
--
Marco
From: mrd
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <Pine.LNX.4.58-035.0405171830460.2429@unix45.andrew.cmu.edu>
On Mon, 17 May 2004, Svein Ove Aas wrote:
> Alan Shutko wrote:
> > Jeff Dalton <····@todday.inf.ed.ac.uk> writes:
> >> Why would anyone expect immediate garbage collection?
> > Perl and Python are both refcounted so things are immediately
> > reclaimed.
> Wait, doesn't refcounting have a bit of trouble with circular structures?
> How do they deal with that?

Crashing is usually a popular option, in the Unix tradition.
From: Svein Ove Aas
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <5dbqc.2119$RL3.55888@news2.e.nsc.no>
mrd wrote:

> On Mon, 17 May 2004, Svein Ove Aas wrote:
>> Alan Shutko wrote:
>> > Jeff Dalton <····@todday.inf.ed.ac.uk> writes:
>> >> Why would anyone expect immediate garbage collection?
>> > Perl and Python are both refcounted so things are immediately
>> > reclaimed.
>> Wait, doesn't refcounting have a bit of trouble with circular
>> structures? How do they deal with that?

I think I'll file this one under "memory leak".

> 
> Crashing is usually a popular option, in the Unix tradition.

Unix tradition may be a distinguished case of "worse is better", but are
there really any options left?

Actually, I think when they say "worse is better", they mean it in the
darwinistic sense. Biology is such a godawful kludge these days, that
it't a wonder we last as long as we do. Of course, lots of the time we
don't...
From: Barry Margolin
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <barmar-C73C68.00170717052004@comcast.dca.giganews.com>
In article <···············@todday.inf.ed.ac.uk>,
 Jeff Dalton <····@todday.inf.ed.ac.uk> wrote:

> Reinhard Gantar <········@uptime.at> writes:
> 
> > Since the let-form has been created by the
> > REPL, it should be gc-ed IMMEDIATELY, just as a
> > form like
> > 
> > ? (+ 42 23)
> > -> 67
> > 
> > is gc-ed right after the evaluation (or is it?)
> 
> I've looked through this thread, and I don't think anyone
> has addressed this point.
> 
> Why would anyone expect immediate garbage collection?
> 
> Is that how things work, somewhere, these days?

I think it would be more proper to say "becomes garbage immediately".  
Garbage doesn't actually get collected until the next time the GC runs.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Joe Marshall
Subject: Re: Closures: I'm not getting it
Date: 
Message-ID: <wu3b6ujr.fsf@ccs.neu.edu>
Reinhard Gantar <········@uptime.at> writes:

> Dear c.l.l.,
>
> in David Lamkins "Successful Lisp", Chapter 11,
> we read a simple example for closures:
>
> ? (let ((e 1))
>      (defun closure-1 () e))
>
> ? (closure-1)
> gives 1.
>
> I know what this does (creating a cons for storing 1,
> binding it to e). The variable e is not visible because
> the scope is limited to the inside of the let.
> So far so good. What I don't understand is why this
> example works. According to what I understood, the
> cons with the 1, together with its binding e should
> be gc-ed as soon as closure-1 is gc-ed, 

So far correct.

> and closure-1 should be gc-ed as soon as the let-form is
> gc-ed. 

Nope.  The closure created by the DEFUN is being stored in the
function cell of the symbol 'closure-1, so this protects it from being
garbage collected.

Now if you did something like (fmakunbound 'closure-1), which removes
the closure from the value cell, then the closure would be
(eventually) collected if no one else had a pointer to it.