From: Tim Bradshaw
Subject: Which implementations can stack-allocate closures?
Date: 
Message-ID: <nkjzodfhgy2.fsf@tfeb.org>
I'm asking this purely out of curiosity.

A lot (all?) of mapping-type functions use their functional argument
with dynamic extent, so it should be possible to stack-allocate any
closure they're called with.  For example:

	(let ((a 0))
	  (map nil #'(lambda (e)
	               (when (numberp e) (incf a e)))
	       l))

Should be able to avoid consing a closure on the heap.

I'm curious as to which implementations can perform this optimisation,
and specifically which implementations can perform it for user-written
functions as well as the CL-defined mapping functions.

Cases I know about:

ACL does a very good job of this.  It can infer from a DYNAMIC-EXTENT
declaration in the function that it can stack-allocate the closure.
You can even (I think) declare a function before it is defined and
callers will stack-allocate.

LispWorks does it for CL-defined functions.  It doesn't *seem* to
listen to DYNAMIC-EXTENT declarations within a user-defined function,
but if you do something like this:

	(let ((f #'(lambda ...)))
	  (declare (dynamic-extent f))
          (user-defined-mapper f ...))

it will stack-allocate, and it's fairly easy to write compiler macros
which generate code like this for user-defined mapping functions.
This is particularly useful for GFs, for which it's hard to see that a
compiler could ever infer dynamic-extentness on its own.  I presume
ACL will stack-allocate under the same circumstances but I haven't
tested that.

CMUCL seems not to do it at all, but I'm not sure - I haven't tested
much.

Genera can do this, but might need nonstandard declarations
(SYS:DOWNWARD-FUNCTION?)

Other implementations I don't know about.  Does anyone know about
them?  Particularly the real story for CMUCL, and CLISP I guess.

Thanks

--tim

From: Kent M Pitman
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <sfwr8yrvf2k.fsf@world.std.com>
Tim Bradshaw <···@tfeb.org> writes:

> I'm asking this purely out of curiosity.
> 
> A lot (all?) of mapping-type functions use their functional argument
> with dynamic extent, so it should be possible to stack-allocate any
> closure they're called with.  For example:
> 
> 	(let ((a 0))
> 	  (map nil #'(lambda (e)
> 	               (when (numberp e) (incf a e)))
> 	       l))
> 
> Should be able to avoid consing a closure on the heap.
> 
> I'm curious as to which implementations can perform this optimisation,
> and specifically which implementations can perform it for user-written
> functions as well as the CL-defined mapping functions.


> Cases I know about:
> 
> ACL does a very good job of this.  It can infer from a DYNAMIC-EXTENT
> declaration in the function that it can stack-allocate the closure.
> You can even (I think) declare a function before it is defined and
> callers will stack-allocate.
> 
> LispWorks does it for CL-defined functions.  It doesn't *seem* to
> listen to DYNAMIC-EXTENT declarations within a user-defined function,
> but if you do something like this:
> 
> 	(let ((f #'(lambda ...)))
> 	  (declare (dynamic-extent f))
>           (user-defined-mapper f ...))
> 
> it will stack-allocate, and it's fairly easy to write compiler macros
> which generate code like this for user-defined mapping functions.
> This is particularly useful for GFs, for which it's hard to see that a
> compiler could ever infer dynamic-extentness on its own.  I presume
> ACL will stack-allocate under the same circumstances but I haven't
> tested that.

LispWorks will also stack allocate, as I expect most implementations will,
if you do

 (flet ((.fn. () ....))
   (declare (dynamic-extent #'.fn.))
   (my-map #'.fn. ...))

So you can write a compiler optimizer on my-map that expands into this
and get the optimization if you don't trust the compiler to give it to you.
And all of that is portable (might not optimize in all implementations, but
should at least run in all implementations).
 
> CMUCL seems not to do it at all, but I'm not sure - I haven't tested
> much.
> 
> Genera can do this, but might need nonstandard declarations
> (SYS:DOWNWARD-FUNCTION?)

Yes, I think that's right.  The facility arose before CL's
dynamic-extent, I'm nearly certain.  Are you suggesting it should use
that declaration instead?  In any case, yes, Genera system functions
use such a declaration and user functions can, too.  It says that the
actual argument will be a downward funarg.  When the compiler sees a
call to a function that has thusly declared one of its arguments with
a lambda expression in it, it's the same as having done (lambda (...)
(declare (sys:downward-funarg)) ...)  or some such thing.  In fact,
I've never liked their sys:downward-funarg declaration because it is
not "scoped" as to "downward from what".  The lambda expression has to
evaluate and its resulting closure be returned up a tiny distance (to
the "eval" call that asked for argument evaluation to occur) before it
goes down again as an argument, and consequently it is not
"downward-only" with respect to the position of the declaration, but
rather with respect to the function call containing it.  Semantically
I prefer the more clumsy syntax CL uses with dynamic-extent, though it
has the problem of having to use FLET and give the function a name...
Sometimes I write macros to hide all that.

---

One problem with just making this assumption from a vanilla dynamic-extent
declaration rather than from a sys:downward-function definition is that
the sys:downward-function definition can be documented to require you to
recompile your callers when you redefine this function; that is, you're
outside the language so normal language promises for modular compilation
don't apply.  The thing about dynamic-extent is that it is not documented
by CL to mean what ACL "helpfully" takes it to mean and what you seem to want
all implementations to take it to mean, that is, that it places a burden on
the caller to recompile.   This is similar, I think, to a situation like:

 (defun g (x) (declare (fixnum x)) ...)

 (defun f (x) (g (+ x 3)))

This might generate a special entry point to g which expects the single
fixnum arg to be passed in a magic place, but if it does, you have to 
accomodate a later redefinition of g without the fixnum declaration so that
f doesn't have to be manually recompiled.  The implementation has gotten 
the caller into the jam by relying on an optimization of its own devising,
and the implementation is responsible (I think) for getting the user out of
the jam.

Likewise, if you decide later to a function argument not be dynamic
extent, and if the language hasn't said that dynamic-extent has this 
extended meaning [which I think it has not], then you have the problem that
your implementation is now passing a stack closure where it isn't wanted.
System functions handle this by being system functions with special contracts
not to change, but user functions don't know they need this.

I've noted there is a constant test of wills between implementors and language
designers in that the designers want the language to be the definition,
and the implementors want the language to be a delivery substrate for 
optimizations.  Sometimes you wish there was a way to introduce a cool
optimization, but from the language design point of view, an optimization
is only an optimization if it does what optimizations do:  transform the
implementation of something into a faster implementation PRESERVING THE 
SEMANTICS.  So the question is always, "what are the semantics"...?  

Most of the uses for which dynamic-extent were originally devised are
intra-function, not inter-function, and it may be over-stretching to say that
this declaration alone should be enough to implicitly require modules to be
recompiled upon what appears to be an isolated redefinition.
From: Tim Bradshaw
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <nkju23ny5oc.fsf@tfeb.org>
Kent M Pitman <······@world.std.com> writes:

> 
> LispWorks will also stack allocate, as I expect most implementations will,
> if you do
> 
>  (flet ((.fn. () ....))
>    (declare (dynamic-extent #'.fn.))
>    (my-map #'.fn. ...))
> 
> So you can write a compiler optimizer on my-map that expands into this
> and get the optimization if you don't trust the compiler to give it to you.
> And all of that is portable (might not optimize in all implementations, but
> should at least run in all implementations).
>  

I'm not sure how this is different from the case I gave?  I had:

  (let ((f #'(lambda ...)))
    (declare (dynamic-extent f))
    (my-map f ...))

- Is this different that what you have?  I have compiler macros thart
expand to this, and I think it's portable.

> Likewise, if you decide later to a function argument not be dynamic
> extent, and if the language hasn't said that dynamic-extent has this 
> extended meaning [which I think it has not], then you have the problem that
> your implementation is now passing a stack closure where it isn't wanted.
> System functions handle this by being system functions with special contracts
> not to change, but user functions don't know they need this.
> 

I agree with this.  I think that I'd like to have the option to say
`please assume that the contract of this function will not change', so
things like dynamic extentness could then be assumed, as could
argument and return types; as well as the option to say (by default)
`please don't assume things that could break'.  As far as I remember
functions defined in a single file do have part of the first
`declaration' made about them, but I don't think there's a way to say
what I really want which is something like `please treat the following
functions / the following package / ... as frozen in the same way that
the CL package is'.  I don't mean that the package is locked but that
the contracts of all the functions &c are.  This is something like
dylan's `sealed' declaration I think.  I've long wished that
implementations would experiment with some kind of sealing notion.

However in the absense of that I kind of like it that ACL will make
this assumpton, though it would be even better if it had a switch
controlling it (which it may do).

--tim
From: Kent M Pitman
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <sfwpuebij9q.fsf@world.std.com>
Tim Bradshaw <···@tfeb.org> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> >  (flet ((.fn. () ....))
> >    (declare (dynamic-extent #'.fn.))
> >    (my-map #'.fn. ...))

> I'm not sure how this is different from the case I gave?  I had:
> 
>   (let ((f #'(lambda ...)))
>     (declare (dynamic-extent f))
>     (my-map f ...))
> 
> - Is this different that what you have?  I have compiler macros thart
> expand to this, and I think it's portable.

Oh, sorry--I thougbt that user-map thing was some implementation-specific
thing.  Yeah, what you wrote is pretty much equivalent to what I did,
except mostly I would never personally write it as a let.  Whenever I'm
going to let a lambda, I always use flet instead.

I do think flet has the advantage of naming the function, for debugging.
 
> [...] I think that I'd like to have the option to say
> `please assume that the contract of this function will not change', so
> things like dynamic extentness could then be assumed, as could
> argument and return types; as well as the option to say (by default)
> `please don't assume things that could break'.  As far as I remember
> functions defined in a single file do have part of the first
> `declaration' made about them, [...]

You're saying that's in the language definition?  Where?

> However in the absense of [sealing] I kind of like it that ACL will make
> this assumpton, though it would be even better if it had a switch
> controlling it (which it may do).

As a rule, I'd bet there are always people to be found who will rally around
and cheer a non-standardism that leads to a speed gain.  Whether that's
good is another question.
From: Barry Margolin
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <wr_C6.2$IL1.417@burlma1-snr2>
In article <···············@world.std.com>,
Kent M Pitman  <······@world.std.com> wrote:
>Tim Bradshaw <···@tfeb.org> writes:
>> [...] I think that I'd like to have the option to say
>> `please assume that the contract of this function will not change', so
>> things like dynamic extentness could then be assumed, as could
>> argument and return types; as well as the option to say (by default)
>> `please don't assume things that could break'.  As far as I remember
>> functions defined in a single file do have part of the first
>> `declaration' made about them, [...]
>
>You're saying that's in the language definition?  Where?

3.2.2.3 Semantic Constrains:

      A call within a file to a named function that is defined in the same
      file refers to that function, unless that function has been declared
      NOTINLINE. The consequences are unspecified if functions are
      redefined individually at run time or multiply defined in the same
      file.

This constraint allows for block compilation of a source file.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Tim Bradshaw
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <nkj4rvn73cz.fsf@tfeb.org>
Kent M Pitman <······@world.std.com> writes:

> 
> I do think flet has the advantage of naming the function, for debugging.
>  

I think the problem with optimising to FLET is that it's quite hard -
for something like

	(my-mapper #'(lambda ...) ...)

it's very easy to write a compiler macro that rewrites it as

	(let ((.my-mapper-f. #'(lambda ...)))
	  (declare ...)
	  (my-mapper .my-mapper-f. ...))

but a fair bit harder to rewrite as an FLET (or at leat more hardness
than I was willing to attempt).

I particularly like it that the obvious approach to such a compiler
macro relies on name capture to avoid infinite recursion, but is also
completely safe.  I get a good feeling from using something that I
teach people how to avoid.

> You're saying that's in the language definition?  Where?

I think Barry answered this bit.

> As a rule, I'd bet there are always people to be found who will rally around
> and cheer a non-standardism that leads to a speed gain.  Whether that's
> good is another question.

Good point.  I wouldn't like to be bundled with those people, though
that's obviously what I've just said.  If I'd thought harder I'd have
phrased it as `only if controlled by a switch'.  Obviously I have
foot-in-mouth disease, despite having had my feet disinfected on
Friday.

--tim
From: Kent M Pitman
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <sfw1yqrnx1y.fsf@world.std.com>
Tim Bradshaw <···@tfeb.org> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > I do think flet has the advantage of naming the function, for debugging.
> 
> I think the problem with optimising to FLET is that it's quite hard -
> for something like
> 
> 	(my-mapper #'(lambda ...) ...)
> 
> it's very easy to write a compiler macro that rewrites it as
> 
> 	(let ((.my-mapper-f. #'(lambda ...)))
> 	  (declare ...)
> 	  (my-mapper .my-mapper-f. ...))
> 
> but a fair bit harder to rewrite as an FLET (or at leat more hardness
> than I was willing to attempt).

 (flet ((.my-mapper-f. ...))
   (declare (dynamic-extent #'.my-mapper-f.))
  (my-mapper #'.my-mapper-f. ...))

is harder?  One of us is surely missing something.
From: Tim Bradshaw
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <nkj1yqr70w7.fsf@tfeb.org>
Kent M Pitman <······@world.std.com> writes:

> 
>  (flet ((.my-mapper-f. ...))
>    (declare (dynamic-extent #'.my-mapper-f.))
>   (my-mapper #'.my-mapper-f. ...))
> 
> is harder?  One of us is surely missing something.

Yes, I think it's harder because the original form was:

(my-mapper <anything-at-all> ...)

so you have to convert <anything-at-all> to something that you can
FLET.  You could do this naively by rewriting to:

	(flet ((.my-mapper-f. (&rest args) (apply <anything-at-all> args)))
           ...)

But that's horrible I think (it also has name capture issues with ARGS
here, obviously fixable).  A better approach would be to fall back to
this in the general case, but to spot a literal function (look for a
list beginning (FUNCTION (LAMBDA ...))) and rewrite appropriately.

(Actually you don't need the &rest bit usually, since the compiler
macro should know the expected signature.)

So I think simply picking up the functional argument, binding it with
LET and then adding a DYNAMIC-EXTENT declaration is much easier to
implement in a macro.

But I may be missing something!

One thing may be that I think this declaration is safe but perhaps I
am wrong about that.  For instance I'm assuming that

	(let ((f #'(lambda (...) ...)))
	  (values 
	   (let ((g f))
	     (declare (dynamic-extent g))
	     (my-mapper g ...))
	   f))

is OK, which I think it is but I haven't read the spec really
carefully there.

--tim
From: Kent M Pitman
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <sfwg0f72qhl.fsf@world.std.com>
Tim Bradshaw <···@tfeb.org> writes:

> Yes, I think it's harder because the original form was:
> 
> (my-mapper <anything-at-all> ...)
> 
> so you have to convert <anything-at-all> to something that you can
> FLET.  You could do this naively by rewriting to:
> 
> 	(flet ((.my-mapper-f. (&rest args) (apply <anything-at-all> args)))
>            ...)
> 

But do you need to rewrite that case at all? I was assuming you were only
rewriting a #'(lambda ...) and specifically in order to declare it.
There's no point to declaring the result of (foo) to be dynamic-extent
because anything returned hy FOO can't have been declared dynamic-extent,
and because you're therefore really not in control of the creation-time.

> One thing may be that I think this declaration is safe but perhaps I
> am wrong about that.  For instance I'm assuming that
> 
> 	(let ((f #'(lambda (...) ...)))
> 	  (values 
> 	   (let ((g f))
> 	     (declare (dynamic-extent g))
> 	     (my-mapper g ...))
> 	   f))
> 
> is OK, which I think it is but I haven't read the spec really
> carefully there.

Yes, I belive it's semantically ok.  You're only responsible for what
you can see in the form that does the binding...  However, it's not
going to get optimized.
From: Tim Bradshaw
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <nkjeluq7bcw.fsf@tfeb.org>
Kent M Pitman <······@world.std.com> writes:

> But do you need to rewrite that case at all? 

No I don't, but doing so makes the compiler macro a fair bit simpler
and has zero cost, which makes me want to.  It's the difference
(for me) between something I can dash off in a few seconds and
something I have to think about and will probably get wrong in some
subtle way because I can't program very well (:-).

Incidentally I checked ACL's documention (www.franz.com somewhere),
and they seem to have very comprehensive control over whether they
infer stack-allocation from DYNAMIC-EXTENT declarations of function
arguments.  There seems to be a general, user-definable function of
the optimization parameters, the default being something sensible
(true if safety < speed I think).  So I think that's a reasonable
approach.

----

I thought a bit last night about why I care (well, I don't *really*
care, but why I'm interested) in this kind of thing and I think what
I'm interesting in is minimising `scripting language syndrome'.
Scripting language syndrome is what happens when a language has
certain built-in features which are implemented incredibly well, using
techniques that are not available to you as a user of the language, or
which are only available by accepting a huge cost, such as
reimplementing the language.

The result of scripting language syndrome is that people are
encouraged to use the built-in things and not to design their own
tools, because those could never approach the performance of the
system-provided thing.  So it discourages abstraction and exploration.

The archetypal case is regular expression support in virtually any
scripting language - there is magic, high-performance support for
regexps which you could never hope to approach by implementing them
within the language.

However there are other senses of this.  C has scripting language
syndrome of syntax - there is magic syntax which is defined by the
language but which you can't change or extend within the language.
This discourages building up the language to solve a problem, because
doing that is so expensive, to the extent that most people don't even
appreciate it as a technique.

Lisp has less scripting language syndrome than any language I know.

The reason I'm interested in things like stack-allocated closures for
user-defined mapping functions, then, is because I'd like to be able
to say something like this to the system: `These functions, types &c
are just as frozen as those exported by the CL package - I promise
that nothing will ever change here again and you can infer whatever
you like about them.  If I change things such that those inferences
are invalid I accept that I will lose.'  So now I can write stuff that
is just like things in the CL package which means I'll never feel any
pressure to not use my nice abstraction, which minimizes scripting
language syndrome even further.  Most CL systems can get quite close
to this for most purposes.

All this sounds like I'm obsessing about performance.  I'm really not
- I can't easily imagine a case where I'd decide not to use an
implementation because it couldn't stack-allocate closures.  I'm just
trying to explain why I'm interested in such things.  At another
level, I *can* imagine a case where I'd find it painful to use an
implementation because my code was just massively slower than the
built-in code - elisp is such an implementation for instance, as is
almost any conventional scripting language.

--tim
From: Rob Warnock
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <9blic7$1capd$1@fido.engr.sgi.com>
Tim Bradshaw  <···@tfeb.org> wrote:
+---------------
| (... why I'm interested) in this kind of thing and I think what
| I'm interesting in is minimising `scripting language syndrome'.
| Scripting language syndrome is what happens when a language has
| certain built-in features which are implemented incredibly well, using
| techniques that are not available to you as a user of the language, or
| which are only available by accepting a huge cost, such as
| reimplementing the language.
| 
| The result of scripting language syndrome is that people are
| encouraged to use the built-in things and not to design their own
| tools, because those could never approach the performance of the
| system-provided thing.  So it discourages abstraction and exploration.
| 
| The archetypal case is regular expression support in virtually any
| scripting language - there is magic, high-performance support for
| regexps which you could never hope to approach by implementing them
| within the language.
...
| Lisp has less scripting language syndrome than any language I know.
+---------------

Well said! Some version of the above description of "scripting
language syndrome" should be archived in a public place
such as <URL:http://www.lisp.org/table/style.htm> or maybe
<URL:http://www.lisp.org/table/compare.htm> or <URL:good.htm>!!


-Rob

-----
Rob Warnock, 31-2-510		····@sgi.com
SGI Network Engineering		<URL:http://reality.sgi.com/rpw3/>
1600 Amphitheatre Pkwy.		Phone: 650-933-1673
Mountain View, CA  94043	PP-ASEL-IA
From: ·········@hotmail.com
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <itk0em1x.fsf@hotmail.com>
Tim Bradshaw <···@tfeb.org> writes:

> Yes, I think it's harder because the original form was:
> 
> (my-mapper <anything-at-all> ...)
> 

The problem here is that <anything-at-all> can't be anything at all.
For instance, you will lose big on this:

(my-mapper #'(lambda (x) #'(lambda (y) (foo x y))) ...)
From: Tim Bradshaw
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <nkjae5brh2k.fsf@tfeb.org>
·········@hotmail.com writes:

> 
> The problem here is that <anything-at-all> can't be anything at all.
> For instance, you will lose big on this:
> 
> (my-mapper #'(lambda (x) #'(lambda (y) (foo x y))) ...)

Good point, I think.  I think the FLET version will lose big too though?

--tim
From: Kent M Pitman
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <sfwu23j7j16.fsf@world.std.com>
Tim Bradshaw <···@tfeb.org> writes:

> ·········@hotmail.com writes:
> 
> > 
> > The problem here is that <anything-at-all> can't be anything at all.
> > For instance, you will lose big on this:
> > 
> > (my-mapper #'(lambda (x) #'(lambda (y) (foo x y))) ...)
> 
> Good point, I think.  I think the FLET version will lose big too though?

I *think* I understand what is being gotten at and this is why I don't
like the 

 (my-mapper #'(lambda (x) (declare (sys:downward-funarg)) ...) ...)

rendition.  It implies but doesn't say for sure what the scope is.  I think
most people think it's the arglist of MY-MAPPER, but no other language 
construct that binds is scoped only to a list of forms; all others require 
a binding form.  When you write it as either the LET or FLET form, you
give a point of binding.  So,

 (let ((f #'(lambda (x) #'(lambda (y) (foo x y)))))
   (declare (dynamic-extent f))
   (mapcar #'funcall (mapcar f xlist) ylist))

or

 (flet ((f (x) #'(lambda (y) (foo x y))))
   (declare (dynamic-extent #'f))
   (mapcar #'funcall (mapcar #'f xlist) ylist))

is fine but 

 (let ((f #'(lambda (x) #'(lambda (y) (foo x y))))) ;BAD
   (declare (dynamic-extent f))
   (mapcar f xlist))

or

 (flet ((f (x) #'(lambda (y) (foo x y)))) ;BAD
   (declare (dynamic-extent #'f))
   (mapcar #'f xlist))

is not.  And the reason it's not is that the resulting set of lambdas
have implicit pointers into the original lambdas, so the declaration 
promise that f is an otherwise inaccessible part (OIP) of f is false
at the time of return from the binding form (the flet). 

Note also that this is a good example (which I'd not thought of before)
f why back-propagating the dynamic-extent-ness of the caller into the
callee is not the right thing.  It's one thing for the mapper to promise
that *it* won't arrange for any heap consing or return of the argument,
but it can't promise that user-intervention won't screw that promise.
So correct optimization of the allocation must rely on information known
at the CREATION time of the object in question, and for all its dataflow
within the scope of that creation form, not just on some random subpath
through that flow and declarations on that subpath.  

It is hard to imagine an appropriate way to win in this case other than
to add yet another declaration which is that a function either does or 
doesn't call any given user functions (whether actual args or hooks
stored in the heap).  If it does, then those would seem to need to be
examined in order to properly reason about this from a dataflow point 
of view.  I'm at a loss offhand for how such a declaration wouuld be
constructed, but it might be fun to hear thoughts on that.
From: Kent M Pitman
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <sfwd7a7o8ql.fsf@world.std.com>
Kent M Pitman <······@world.std.com> writes:

> So,
> 
>  (let ((f #'(lambda (x) #'(lambda (y) (foo x y)))))
>    (declare (dynamic-extent f))
>    (mapcar #'funcall (mapcar f xlist) ylist))
> 
> or
> 
>  (flet ((f (x) #'(lambda (y) (foo x y))))
>    (declare (dynamic-extent #'f))
>    (mapcar #'funcall (mapcar #'f xlist) ylist))
> 
> is fine but 
> 
>  (let ((f #'(lambda (x) #'(lambda (y) (foo x y))))) ;BAD
>    (declare (dynamic-extent f))
>    (mapcar f xlist))
> 
> or
> 
>  (flet ((f (x) #'(lambda (y) (foo x y)))) ;BAD
>    (declare (dynamic-extent #'f))
>    (mapcar #'f xlist))
> 
> is not.

Actually, the more I think about it, the more it comes back to me that
implementations have something in them where when they go to arrange for
the downward closure, they look to see if it will yield a closure that
might not be downward; if they do, I think they have to cancel the down-ness
of the allocation or arrange for the downward closure's storage to be 
evacuated to the heap so that it can be closed over by the inner closure.

Hopefully one of the people who's been involved in implementation of this
sort of thing will comment...  I think implementing closures like CL has
is a lot more work than it looks like to the casual observer, and this is
one hint as to why.  But my recollection is that existing implementations 
do a lot of work in this regard--I just can't recall what the promise is.

So perhaps I was jsut wrong in saying the above items are BAD.
From: Marius Vollmer
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <87d7a7pcr2.fsf@zagadka.ping.de>
Kent M Pitman <······@world.std.com> writes:

> It is hard to imagine an appropriate way to win in this case other
> than to add yet another declaration [...]

Wouldn't in this case most issues with dynamic-extent declarations
disappear when `mapcar' or `my-mapper' are inlined by the compiler? (A
compiler-macro could help with this.)  The compiler could then infer
on its own that it can stack allocate the closure, or perform more
radical optimizations.
From: Kent M Pitman
Subject: Re: Which implementations can stack-allocate closures?
Date: 
Message-ID: <sfwbspre055.fsf@world.std.com>
Marius Vollmer <···@zagadka.ping.de> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > It is hard to imagine an appropriate way to win in this case other
> > than to add yet another declaration [...]
> 
> Wouldn't in this case most issues with dynamic-extent declarations
> disappear when `mapcar' or `my-mapper' are inlined by the compiler? (A
> compiler-macro could help with this.)  The compiler could then infer
> on its own that it can stack allocate the closure, or perform more
> radical optimizations.

Inlining is a red herring in that it's an orthogoonal process.   Second,
it occurs only for functios that are inlinable.  MAPCAR might be one.
Some user-defined mappers might be others, but not all...
Also, inlniing may be inhibited for any number of reasons.