[comp.lang.lisp] flet, a question of style?

rit@killdeer.Stanford.EDU (Jean-Francois Rit) (08/04/90)

What is a good use of flet?

The way I see it, the advantages of flet over an external defun are:

- cleanliness of code: the local function is defined right where it is only
  needed and that is made clear to the reader.

- Use of lexical context: when the flet is defined in the right place, a lot
  of local variables need not be passed to the function.

The cons are:

- It is local (obviously)
- It is more expensive.

Here is the heart of my question. Is it really so, does it always have to be?
My own limited tests gave me a bit less than 10% in disfavor of flet. I also
noticed that an flet form in the do clause of a MIT loop is not compiled.


(defun with-flet (n m)
  (let ((x (make-array 1)))
    (flet ((titi-flet (p)
	     (loop for i from 0 to n
	       do (setf (aref x 0) (+ p i)))))
      (loop for j from 0 to m
	do (titi-flet j)))))

(defun without-flet (n m)
  (let ((x (make-array 1)))
    (loop for j from 0 to m
      do (titi j n x))))

(defun titi (p n x)
  (loop for i from 0 to n
    do (setf (aref x 0) (+ p i))))


Anyhow, do the programming masters use or advocate the use of flet
like they would for , say, let?
 

gumby@Cygnus.COM (David Vinayak Wallace) (08/05/90)

   Date: Sat, 4 Aug 90 00:19:13 GMT
   From: rit@killdeer.Stanford.EDU (Jean-Francois Rit)

   The way I see it, the advantages of flet over an external defun are:

   - cleanliness of code: the local function is defined right where it is only
     needed and that is made clear to the reader.

Unfortunately in a large system you then end up with a rats-nest of
nested functions.  The package system (poorly) addresses this issue.

   - Use of lexical context: when the flet is defined in the right place, a lot
     of local variables need not be passed to the function.

Not only that but it allows you to side-effect variables in its
lexical environment, which is really handy.

   - It is more expensive.

There's no reason why it need be.  In the case where the lexical
function is not passed down the stack it can be open-coded.  

And if you know that the function will be passed down but never up,
then it can share the environment of its enclosing function.
Unfortunately you can't do this on register-window machines like the
SPARC.  On those machines downward funarg can be somewhat expensive.

pierson@encore.com (Dan L. Pierson) (08/05/90)

In article <1990Aug4.001913.22597@Neon.Stanford.EDU> rit@killdeer.Stanford.EDU (Jean-Francois Rit) writes:

(about a FLET being more expensive than a DEFUN)

   Here is the heart of my question. Is it really so, does it always
   have to be?  My own limited tests gave me a bit less than 10% in
   disfavor of flet. I also noticed that an flet form in the do clause
   of a MIT loop is not compiled.

What Lisp system did you measure?  With what OPTIMIZE settings?  It's
not obvious that calling a FLET should be more expensive, if it's
inlined it should be less.

The failure to compile the FLET in the LOOP sounds like a compiler bug
to me.

--

                                            dan

In real life: Dan Pierson, Encore Computer Corporation, Research
UUCP: {talcott,linus,necis,decvax}!encore!pierson
Internet: pierson@encore.com

lou@atanasoff.rutgers.edu (Lou Steinberg) (08/07/90)

In article <1990Aug4.001913.22597@Neon.Stanford.EDU> rit@killdeer.Stanford.EDU (Jean-Francois Rit) writes:

> What is a good use of flet? [...]  The cons are: [...]

One aspect that hasn't come up yet in responses (at least those I've
seen) is the fact that many Lisp programming environments do not
support local function definitions (i.e. defined via labels and flet)
nearly as well as they do global definitions.  

This shows up in at least two ways.  The first is that often you can't
break, trace, or advise a local function.  The reason is that the
break/trace function can easily access a global function from the
symbol that is it's name, and can then wrap that definition in
break/trace code.  For it to find a local definition requires it to
wade through the code of the enclosing function.  Not that it can't be
done, but it is significantly more complex and/or less portable, and
thus less often supported.

(By the way, this problem is even worse for anonymous lambdas - at
least the local functions have a name, if only locally.)

The other way this poorer-support of local definitions shows up is in
the incremental-load-compile facility that many lisps (or lisp/emacs
combinations) support.  It is useful to be able to modify one
function, then load (and maybe compile) the modified definition
without reloading/recompiling the whole program.  I don't know of any
lisp that allows you to do this with a local definition.  In fact, if
you were to go to the extreme and make all functions local except a
single top level "main" function, as is normal in Pascal and such
languages, then if you change anything you have to reload/recompile
the whole program.  (This is why even languages where local
definitions are the normal case will allow non-local definitions as
part of a separate-compilation facility.)
-- 
					Lou Steinberg

uucp:   {pretty much any major site}!rutgers!aramis.rutgers.edu!lou 
arpa:   lou@cs.rutgers.edu

michaelg@Neon.Stanford.EDU (Michael Greenwald) (08/14/90)

lou@atanasoff.rutgers.edu (Lou Steinberg) writes:

>The other way this poorer-support of local definitions shows up is in
>the incremental-load-compile facility that many lisps (or lisp/emacs
>combinations) support.  It is useful to be able to modify one
>function, then load (and maybe compile) the modified definition
>without reloading/recompiling the whole program.  I don't know of any
>lisp that allows you to do this with a local definition.  

Symbolics Common Lisp allows you to do this indirectly.  You can
ADVISE a local function ('(:INTERNAL FOO 0) is the function spec for
the first function defined by FLET, LABELS, or LAMBDA inside FOO.  You
can use (:INTERNAL FOO 0 localname) if the function has a local name).
If you compile the :AROUND advice, and never actually call the
original code, you've essentially replaced the original local
definition.  Of course, you don't get access to lexically apparent
variables, so ADVISE is probably not what you meant,

If you >do< want to access the local variables of the parent, then it
would be hard to see how to easily implement the local compilation
without explicitly passing in the parents environment.  

I guess my real question is: how often would you >want< to modify and
compile an internal definition without its parent?  The cost of
modifying the parent, also, can't be that large unless something is
>really< strange about your program.

jeff@aiai.ed.ac.uk (Jeff Dalton) (08/15/90)

In article <1990Aug13.201712.19721@Neon.Stanford.EDU> michaelg@Neon.Stanford.EDU (Michael Greenwald) writes:

>I guess my real question is: how often would you >want< to modify and
>compile an internal definition without its parent?  The cost of
>modifying the parent, also, can't be that large unless something is
>>really< strange about your program.

If there was better support for it in Common Lisp implementations,
I might want to write "modules" like this (perhaps with the aid of
some macros):


    (let (...)             ; private variables

      (labels (...)        ; private functions

        (defun ...)        ; public functions
        ... ))

So the "parent" might be quite large.

-- Jeff

michaelg@Neon.Stanford.EDU (Michael Greenwald) (08/15/90)

jeff@aiai.ed.ac.uk (Jeff Dalton) writes:

>If there was better support for it in Common Lisp implementations,
>I might want to write "modules" like this (perhaps with the aid of
>some macros):


>    (let (...)             ; private variables

>      (labels (...)        ; private functions

>        (defun ...)        ; public functions
>        ... ))

>So the "parent" might be quite large.

It's possible I'm not understanding you clearly.  Why isn't what you
describe simply a single instance of a class?  The private variables
are slots, the private functions are GENERIC-LABELS (or in pre-CLOS
flavors, DEFUN-IN-FLAVOR's) and the DEFUNs are DEFMETHODs?  It was
certainly the case that you did not have to recompile the
DEFUN-IN-FLAVORs every time you recompiled a defmethod.  I assume
(hope) that GENERIC-LABELS is similar.