From: Dr. Edmund Weitz
Subject: compiled-function-p and CMUCL
Date: 
Message-ID: <m3ofgpdkgn.fsf@dyn138.dbdmedia.de>
[I sent this to the cmucl-help mailing list but got no reply, so I'm
trying it here.]

Hi!

I'm working on a small program that generates code at runtime from
user-supplied-data. I wanted to give the user the option to compile
the generated functions before applying them. As this option is set
once and the functions might be applied a lot of times I wanted to
test whether they have already been compiled but had to found out that
COMPILED-FUNCTION-P doesn't help me with CMUCL.

I found this old message by Barry Margolin which explains the
situation

  <http://groups.google.com/groups?selm=5gv26j%24dv0%40tools.bbnplanet.com>

but from my limited knowledge I wonder whether this is the whole
story. By examining the created functions in the listener and timing
them it looks to me as if there really _is_ a difference between the
interpreted and the compiled functions. As far as I understand it
CMUCL always compiles functions to some kind of intermediate code but
only compiles to machine code if explicitely asked to do so.  That's
different from, say, MCL or CormanLisp where functions are always
compiled to machine code on-the-fly. (Correct me if I'm wrong.)

So, anyway, would the following code be the correct way (compatible
with earlier CMUCL versions) to deal with my situation?

  (defun my-compiled-function-p (fn)
    #-cmu (compiled-function-p fn)
    #+cmu (not (eq (type-of fn) 'eval:interpreted-function)))

or am I missing something. (I'm currently using 18d-pre from
2002-01-15.)

Thanks,
Edi.


USER> (defparameter foo (coerce '(lambda () (loop for n below 1000 sum (* n n))) 'function))
FOO
USER> foo
#<Interpreted Function (LAMBDA () (LOOP FOR N BELOW 1000 SUM #)) {48007D71}>
USER> (defparameter bar (compile nil foo))
Compiling LAMBDA NIL: 
Compiling Top-Level Form: 

BAR
USER> bar
#<Function "LAMBDA NIL" {48059889}>
USER> (compiled-function-p foo)
T
USER> (compiled-function-p bar)
T
USER> (eq (type-of foo) 'eval:interpreted-function)
T
USER> (eq (type-of bar) 'eval:interpreted-function)
NIL
USER> (time (apply foo))
Compiling LAMBDA NIL: 
Compiling Top-Level Form: 

Evaluation took:
  0.71 seconds of real time
  0.01 seconds of user run time
  0.0 seconds of system run time
  5 page faults and
  122848 bytes consed.
332833500
USER> (time (apply bar))
Compiling LAMBDA NIL: 
Compiling Top-Level Form: 

Evaluation took:
  0.0 seconds of real time
  0.0 seconds of user run time
  0.0 seconds of system run time
  0 page faults and
  0 bytes consed.
332833500

-- 

Dr. Edmund Weitz
Hamburg
Germany

The Common Lisp Cookbook
<http://cl-cookbook.sourceforge.net/>

From: Thomas F. Burdick
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <xcv662wmwva.fsf@apocalypse.OCF.Berkeley.EDU>
···@agharta.de (Dr. Edmund Weitz) writes:

> [I sent this to the cmucl-help mailing list but got no reply, so I'm
> trying it here.]

I could have sworn that I'd answered this ... checking now, I see that
I was having network problems and forgot to finish and send the e-mail
later, oops.

> I'm working on a small program that generates code at runtime from
> user-supplied-data. I wanted to give the user the option to compile
> the generated functions before applying them. As this option is set
> once and the functions might be applied a lot of times I wanted to
> test whether they have already been compiled but had to found out that
> COMPILED-FUNCTION-P doesn't help me with CMUCL.

There's no reliable way to do this with type discrimination, that I
know of.  However, there are two reasonable, portable ways I can think
of off the top of my head to do what you want.  The first, instead of
APPLYing functions directly, write your own APPLY, and wrap the
function in a little class:

  (defclass my-function ()
    ((function :initarg function :accessor function-function)
     (compiled? :initarg compiled? :accessor compiled?)))

  (defvar *compile-first-p* nil)

  (defmethod my-apply (fn &rest args)
    (apply fn args))

  (defmethod my-apply ((fn my-function) &rest args)
    (when (and (not (compiled? fn))
               *compile-first-p*)
      (setf (function-function fn) (compile nil (function-function fn))))
    (apply (function-function fn) args))

or just call compile each time:

  (defun my-apply (fn &rest args)
    (apply (compile nil fn) args))

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Dr. Edmund Weitz
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <m3k7ra38a1.fsf@bird.agharta.de>
···@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

> There's no reliable way to do this with type discrimination, that I
> know of.  However, there are two reasonable, portable ways I can
> think of off the top of my head to do what you want.  The first,
> instead of APPLYing functions directly, write your own APPLY, and
> wrap the function in a little class:
> 
>   (defclass my-function ()
>     ((function :initarg function :accessor function-function)
>      (compiled? :initarg compiled? :accessor compiled?)))
> 
>   (defvar *compile-first-p* nil)
> 
>   (defmethod my-apply (fn &rest args)
>     (apply fn args))
> 
>   (defmethod my-apply ((fn my-function) &rest args)
>     (when (and (not (compiled? fn))
>                *compile-first-p*)
>       (setf (function-function fn) (compile nil (function-function fn))))
>     (apply (function-function fn) args))

This is basically what I did already 'cause my function already lives
in a class. For some strange reason I thought it would be more
'elegant' - or rather less redundant - to spare this one bit of
information and query the function object itself. That's were my
initial question came from.

> or just call compile each time:
> 
>   (defun my-apply (fn &rest args)
>     (apply (compile nil fn) args))

I hadn't thought about this, compiling on each invocation, 'cause I
thought compilation might be costly. Now I thought I was extra-smart
and tried something like the following:

  USER> (setq f (coerce '(lambda (x) (* x x)) 'function))
  
  #<Interpreted Function (LAMBDA (X) (* X X)) {484A9EA9}>
  USER> (setq f (compile nil f))
  Compiling LAMBDA (X): 
  Compiling Top-Level Form: 
  
  #<Function "LAMBDA (X)" {484B9F39}>
  USER> (setq f (compile nil f))
  Compiling LAMBDA (X): 
  Compiling Top-Level Form: 
  
  #<Function "LAMBDA (X)" {484CB019}>

which looks suspiciously as if CMUCL (pre-18d) would compile f on each
invocation of COMPILE which is what I wanted to avoid in the first
place. LispWorks (4.2 trial) behaves differently:

  CL-USER 1 > (setq f (coerce '(lambda (x) (* x x)) 'function))
  #'(LAMBDA (X) (* X X))
  
  CL-USER 2 > (setq f (compile nil f))
  #<function 12 20668352>
  
  CL-USER 3 > (setq f (compile nil f))
  ;;;*** Warning between functions:   The definition supplied, #<function 12 20668352>, is already compiled.
  #<function 12 20668352>
  
(This is what I actually expected - or rather wanted to have.)
AllegroCL (6.1 trial) raises an error:

  CL-USER(1): (setq f (coerce '(lambda (x) (* x x)) 'function))
  #<Interpreted Function (unnamed) @ #x715062ea>
  CL-USER(2): (setq f (compile nil f))
  #<Function (:ANONYMOUS-LAMBDA 0) @ #x714527ba>
  CL-USER(3): (setq f (compile nil f))
  Error: Illegal syntax for function:
         #<Function (:ANONYMOUS-LAMBDA 0) @ #x714527ba>
    [condition type: SIMPLE-ERROR]
  
While I couldn't find anything in the CLHS where it is explicitely
forbidden for the second argument to COMPILE to be a compiled
function, I also cannot find a place to convince me that ACL's
behavior is wrong. (The HyperSpec entry for COMPILE says "Compiles an
interpreted function".) So I think I'll better stick to the first
solution (using a flag like ALREADY-COMPILED-P) in order to have an
ANSI-compliant solution.

Any comments?

Thanks for your time,
Edi.
 
-- 

Dr. Edmund Weitz
Hamburg
Germany

The Common Lisp Cookbook
<http://cl-cookbook.sourceforge.net/>
From: Pierpaolo BERNARDI
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <wdfu8.18060$vF6.589208@news2.tin.it>
"Dr. Edmund Weitz" <···@agharta.de> ha scritto nel messaggio ···················@bird.agharta.de...

>   USER> (setq f (coerce '(lambda (x) (* x x)) 'function))

Why you use this, instead of (SETQ F (LAMBDA (X) (* X X))) ?

> While I couldn't find anything in the CLHS where it is explicitely
> forbidden for the second argument to COMPILE to be a compiled
> function, I also cannot find a place to convince me that ACL's
> behavior is wrong.

The COMPILE page says:

    "If the definition is already a compiled function, compile
    either produces that function itself (i.e., is an identity operation)
    or an equivalent function."

Seems explicit enough to me.

Cheers
P.
From: Dr. Edmund Weitz
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <m3g01y2wyo.fsf@bird.agharta.de>
"Pierpaolo BERNARDI" <··················@hotmail.com> writes:

> "Dr. Edmund Weitz" <···@agharta.de> ha scritto nel messaggio
> ···················@bird.agharta.de...
> 
> >   USER> (setq f (coerce '(lambda (x) (* x x)) 'function))
> 
> Why you use this, instead of (SETQ F (LAMBDA (X) (* X X))) ?

Oh, that was a remnant of my original program where the code was
generated at run time. For this particular example, your variant would
have been enough, yes.

> > While I couldn't find anything in the CLHS where it is explicitely
> > forbidden for the second argument to COMPILE to be a compiled
> > function, I also cannot find a place to convince me that ACL's
> > behavior is wrong.
> 
> The COMPILE page says:
> 
>     "If the definition is already a compiled function, compile
>     either produces that function itself (i.e., is an identity
>     operation) or an equivalent function."
> 
> Seems explicit enough to me.

Ooops, I shouldn't read the HyperSpec while my little daughter is on
my lap and reaching for the keyboard... :)

So, can we deduce from this that ACL's behaviour is wrong here? I
repeat my example here for those who don't want to go back in this
thread:

  CL-USER(1): (setq f (coerce '(lambda (x) (* x x)) 'function))
  #<Interpreted Function (unnamed) @ #x715062ea>
  CL-USER(2): (setq f (compile nil f))
  #<Function (:ANONYMOUS-LAMBDA 0) @ #x714527ba>
  CL-USER(3): (setq f (compile nil f))
  Error: Illegal syntax for function:
         #<Function (:ANONYMOUS-LAMBDA 0) @ #x714527ba>
    [condition type: SIMPLE-ERROR]

Thanks,
Edi.

-- 

Dr. Edmund Weitz
Hamburg
Germany

The Common Lisp Cookbook
<http://cl-cookbook.sourceforge.net/>
From: Dr. Edmund Weitz
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <m33cxsogft.fsf@bird.agharta.de>
···@agharta.de (Dr. Edmund Weitz) writes:

> So, can we deduce from this that ACL's behaviour is wrong here? I
> repeat my example here for those who don't want to go back in this
> thread:
> 
>   CL-USER(1): (setq f (coerce '(lambda (x) (* x x)) 'function))
>   #<Interpreted Function (unnamed) @ #x715062ea>
>   CL-USER(2): (setq f (compile nil f))
>   #<Function (:ANONYMOUS-LAMBDA 0) @ #x714527ba>
>   CL-USER(3): (setq f (compile nil f))
>   Error: Illegal syntax for function:
>          #<Function (:ANONYMOUS-LAMBDA 0) @ #x714527ba>
>     [condition type: SIMPLE-ERROR]

The fine folks at Franz Inc. have acknowledged this to be a bug. They
expect it to be fixed in the next release, AllegroCL 6.2, which should
be out later this year.

Just to provide a happy ending to this thread in case someone is
reading it on Google some months or years later... :)

Edi.

-- 

Dr. Edmund Weitz
Hamburg
Germany

The Common Lisp Cookbook
<http://cl-cookbook.sourceforge.net/>
From: Kent M Pitman
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <sfw3cy0vq9n.fsf@shell01.TheWorld.com>
···@agharta.de (Dr. Edmund Weitz) writes:

> USER> (defparameter foo 
>         (coerce '(lambda () (loop for n below 1000 sum (* n n)))
>                 'function))
> FOO

[Aside: DON'T DO THIS.  Call the parameter *FOO*.
 As a good general rule (there are exceptions, but they are rare), you should
 NEVER proclaim a variable special [as DEFPARAMETER does implicitly] unless it
 has *'s around its name.  If you have not planned for it, this will bite you
 later in very bad ways.]

> USER> foo
> #<Interpreted Function (LAMBDA () (LOOP FOR N BELOW 1000 SUM #)) {48007D71}>
>
> USER> (defparameter bar (compile nil foo))
> Compiling LAMBDA NIL: 
> Compiling Top-Level Form: 
> BAR

[Ugh. *BAR*]

> USER> bar
> #<Function "LAMBDA NIL" {48059889}>
>
> USER> (compiled-function-p foo)
> T
> USER> (compiled-function-p bar)
> T

This just sounds like something you could argue was a bug in
COMPILED-FUNCTION-P.  Though there are some quite reasonable reasons
that a vendor/implementor could use to argue that it should return T.
Strictly, their compiled functions might be enough pre-processed that
they could qualify as compiled functions; 
see CLHS 3.2.2.2 "Minimal Compilation" for clarification.

> USER> (eq (type-of foo) 'eval:interpreted-function)
> T
> USER> (eq (type-of bar) 'eval:interpreted-function)
> NIL
> USER> (time (apply foo))

[Aside: Don't do this. It's non-standard to assume APPLY takes 
 no second argument. APPLY is required by the standard to have at
 least one additional arg beyond the function.  Apparently CMUCL
 is assuming '() but that's a non-standard behavior.]

> Compiling LAMBDA NIL: 
> Compiling Top-Level Form: 
> 
> Evaluation took:
>   0.71 seconds of real time
>   0.01 seconds of user run time
>   0.0 seconds of system run time
>   5 page faults and
>   122848 bytes consed.
> 332833500
> USER> (time (apply bar))

[Ugh]

> Compiling LAMBDA NIL: 
> Compiling Top-Level Form: 
> 
> Evaluation took:
>   0.0 seconds of real time
>   0.0 seconds of user run time
>   0.0 seconds of system run time
>   0 page faults and
>   0 bytes consed.
> 332833500

You should know that there are many different levels of compilation and
that compiled-function-p does not act as predicate for the concept 
"could I make this function faster by getting its source and compiling
it myself using parameters of my choice" so there's still something wrong
with your expecting this to work.  Ultimately, there is no way you can 
know if compilation is required for _speed_ purposes; and, indeed, if there
were such a function, there is also no function that will reliably disclose
the source code and context such that you could repair the problem anyway.

COMPILED-FUNCTION-P is really answering the questions "does this function
no longer depend on oddities of its source such as macro expansion", which
is not the question you probably mean it to answer.  Again, see 3.2.2.2
on minimal compilation.

You can make a casual argument that COMPILED-FUNCTION-P might help people
muddle through better with their confused expectation about the boundary
between compiled and interpreted functions if it returned a different value.
But ultimately I suspect this return value has forced you to do some research
and learn some things that you should probably know about the real truth.

If I recall, LispWorks pre-processes the whole form removing macros before
setting up interpreted functions, while Allegro does not.  This leads to
results that are confusing if you think both are interpreted.  I have
sometimes argued that LispWorks, even though it will run an interpreter,
is still compiled code.  Understanding this would make the Allegro interpreter
look less wrong.  (I hope I'm remembering correctly which of these does
this.)

The thing to understand is that "an interpreter will be used" is
orthogonal to the question of "this function is compiled" as
compilation is defined by CL.  Whether an interpreter will be used is
an implementation detail not important to user programs; whether a
program depends on its compilation environment to have been or to
still be loaded is not an implementation detail... If you're
considering whether to load (or blow away) the macro support for
something, then it matters that you know whether the clients of that
macro have all been compiled.  It does not matter at all from the
point of view of "semantics" (which oddly never seem to include
performance) whether something is fast or slow, which is all that is
gated by use of an interpreter.  clisp is all "interpreted" (byte
code) and runs generally quite fast, at least by some metrics, so it's
not _even_ like interpretation is a guaranteed hint of slowness.  It's
a remarkably complex space to discuss and it's well ignored by
pretending to have user predicates that could produce a single
informational bit of probably-uncomputable information, that is, "is
this function fast enough?".
From: Dr. Edmund Weitz
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <m3bscm2vpy.fsf@bird.agharta.de>
Kent M Pitman <······@world.std.com> writes:

> ···@agharta.de (Dr. Edmund Weitz) writes:
> 
> > USER> (defparameter foo 
> >         (coerce '(lambda () (loop for n below 1000 sum (* n n)))
> >                 'function))
> > FOO
> 
> [Aside: DON'T DO THIS.  Call the parameter *FOO*.  As a good general
> rule (there are exceptions, but they are rare), you should NEVER
> proclaim a variable special [as DEFPARAMETER does implicitly] unless
> it has *'s around its name.  If you have not planned for it, this
> will bite you later in very bad ways.]

Yep, I do this in all of my programs. I didn't bother to do it here
because this was just a small snippet from a listener session to
illustrate my point.

> > USER> (time (apply foo))
> 
> [Aside: Don't do this. It's non-standard to assume APPLY takes no
> second argument. APPLY is required by the standard to have at least
> one additional arg beyond the function.  Apparently CMUCL is
> assuming '() but that's a non-standard behavior.]

Thanks, I wasn't aware of this.

> You should know that there are many different levels of compilation
> and that compiled-function-p does not act as predicate for the
> concept "could I make this function faster by getting its source and
> compiling it myself using parameters of my choice" so there's still
> something wrong with your expecting this to work.  Ultimately, there
> is no way you can know if compilation is required for _speed_
> purposes; and, indeed, if there were such a function, there is also
> no function that will reliably disclose the source code and context
> such that you could repair the problem anyway.
> 
> COMPILED-FUNCTION-P is really answering the questions "does this
> function no longer depend on oddities of its source such as macro
> expansion", which is not the question you probably mean it to
> answer.  Again, see 3.2.2.2 on minimal compilation.
> 
> You can make a casual argument that COMPILED-FUNCTION-P might help
> people muddle through better with their confused expectation about
> the boundary between compiled and interpreted functions if it
> returned a different value.  But ultimately I suspect this return
> value has forced you to do some research and learn some things that
> you should probably know about the real truth.
> 
> If I recall, LispWorks pre-processes the whole form removing macros
> before setting up interpreted functions, while Allegro does not.
> This leads to results that are confusing if you think both are
> interpreted.  I have sometimes argued that LispWorks, even though it
> will run an interpreter, is still compiled code.  Understanding this
> would make the Allegro interpreter look less wrong.  (I hope I'm
> remembering correctly which of these does this.)
> 
> The thing to understand is that "an interpreter will be used" is
> orthogonal to the question of "this function is compiled" as
> compilation is defined by CL.  Whether an interpreter will be used
> is an implementation detail not important to user programs; whether
> a program depends on its compilation environment to have been or to
> still be loaded is not an implementation detail... If you're
> considering whether to load (or blow away) the macro support for
> something, then it matters that you know whether the clients of that
> macro have all been compiled.  It does not matter at all from the
> point of view of "semantics" (which oddly never seem to include
> performance) whether something is fast or slow, which is all that is
> gated by use of an interpreter.  clisp is all "interpreted" (byte
> code) and runs generally quite fast, at least by some metrics, so
> it's not _even_ like interpretation is a guaranteed hint of
> slowness.  It's a remarkably complex space to discuss and it's well
> ignored by pretending to have user predicates that could produce a
> single informational bit of probably-uncomputable information, that
> is, "is this function fast enough?".

I think I got your point, and even before I read your posting it was
pretty clear to me that CMUCL didn't do anything wrong or forbidden
with respect to the ANSI standard here.

Nevertheless, in my understanding, compilation is mainly used to
increase the execution speed of a function or program, so it would
have been nice if there had been a portable way to ask the function
whether it has been "fully" compiled yet (with respect to the
implementation). Never mind - I see there are reasons why this would
be difficult and/or impossible and I've found a solution for my
particular problem - see Thomas F. Burdick's posting.

Thanks,
Edi.

PS: Upon re-reading you last paragraph let me add for clarity that I'm
not looking for a magical IS-THIS-FAST-ENOUGH-P function. What I'd
like to have is something like
WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P...

---

Dr. Edmund Weitz
Hamburg
Germany

The Common Lisp Cookbook
<http://cl-cookbook.sourceforge.net/>
From: Pierre R. Mai
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <874riemdzy.fsf@orion.bln.pmsf.de>
···@agharta.de (Dr. Edmund Weitz) writes:

> PS: Upon re-reading you last paragraph let me add for clarity that I'm
> not looking for a magical IS-THIS-FAST-ENOUGH-P function. What I'd
> like to have is something like
> WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P...

While on the surface that might seem a more reasonable thing to
standardize, it probably isn't:

Imagine (and this isn't something totally unrealistic) an
implementation that uses profiling information gathered at run-time in
order to optimize further compilation[1].  Such an implementation would
often have a hard time deciding whether recompilation would yield the
same code, until after the fact.  In such an implementation calling
your predicate might be just as expensive as doing the compilation
itself, hence invalidating your assumptions.

So I think that you'll always have to rely on implementation-dependent
assumptions anyway, hence using implementation-dependent predicates
doesn't seem too bad...

That said, I think that CMU CL's behaviour is in fact not conforming,
because it currently treats compiled-function-p as a predicate that
tests for the type compiled-function, but that is defined as a synonym
for the (primitive) type function.  Hence CMU CL will yield T on all
functions, even those that haven't been processed in a way that
satisfies ANSI's requirements for minimal compilation.

It is my understanding/interpretation/opinion, that an implementation
should yield T for compiled-function-p only for code that has been
processed in a way that satisfies those requirements, since they are
fairly meaningful requirements.  In other words, if I have a function
that is compiled-function-p, I should be able to blow away all macro
(and compiler-macro) definitions that it uses, and it should still
work.

I'll have to investigate further to find a fix for this, since messing
with primitive types and/or compiled-function-p is a bit intricate...

Regs, Pierre.


Footnotes: 
[1]  Actually, it suffices to have an implementation that uses global
     declarations to optimize compilation.  Changed or added
     declarations between calls to compile are possibly going to
     result in different code.  This is e.g. true of CMU CL, and most
     other implementations.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Dr. Edmund Weitz
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <m37kna2luz.fsf@bird.agharta.de>
"Pierre R. Mai" <····@acm.org> writes:

> ···@agharta.de (Dr. Edmund Weitz) writes:
> 
> > PS: Upon re-reading you last paragraph let me add for clarity that
> > I'm not looking for a magical IS-THIS-FAST-ENOUGH-P function. What
> > I'd like to have is something like
> > WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P...
> 
> While on the surface that might seem a more reasonable thing to
> standardize, it probably isn't:
> 
> Imagine (and this isn't something totally unrealistic) an
> implementation that uses profiling information gathered at run-time
> in order to optimize further compilation[1].  Such an implementation
> would often have a hard time deciding whether recompilation would
> yield the same code, until after the fact.  In such an
> implementation calling your predicate might be just as expensive as
> doing the compilation itself, hence invalidating your assumptions.
> 
> So I think that you'll always have to rely on
> implementation-dependent assumptions anyway, hence using
> implementation-dependent predicates doesn't seem too bad...
> 
> [...]
> 
> Footnotes: 
> [1]  Actually, it suffices to have an implementation that uses global
>      declarations to optimize compilation.  Changed or added
>      declarations between calls to compile are possibly going to
>      result in different code.  This is e.g. true of CMU CL, and most
>      other implementations.

The more I think about it, the less clear it is to me. My current
understanding is that my hypothetical function
WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P is actually
the same as COMPILED-FUNCTION-P according to the HyperSpec - at least
as far as I understand the term 'equivalent'[1] there:

The HyperSpec entry for COMPILE says "If the definition is already a
compiled function, COMPILE either produces that function itself (i.e.,
is an identity operation) or an equivalent function." And, according
to the glossary, a 'compiled function' is an object of type
COMPILED-FUNCTION, i.e. an object is a compiled function if and only
if COMPILED-FUNCTION-P applied to this object returns true.

So, in my original example, FOO was of type EVAL:INTERPRETED-FUNCTION
while BAR wasn't, but CMUCL[2] said that FOO was a compiled function,
and BAR resulted from applying COMPILE to FOO which should yield an
object 'equivalent' to FOO. I wonder if two objects that are clearly
not of the same type can be equivalent in this case.

On the other hand, I agree with you that the usage of global compiler
declarations renders the implementation of something like
WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P almost
impossible. But then again, isn't the same true for
COMPILED-FUNCTION-P already if you follow my arguments above?

Thanks,
Edi.

[1] I've briefly scanned the introduction of the CLHS to find a
    definition of the term 'equivalent' but couldn't find it. Maybe if
    I had found it, that would clarify the situation.

[2] Note that I'm not interested in proving CMUCL (or any
    implementation) wrong - I just want to understand this particular
    topic.


-- 

Dr. Edmund Weitz
Hamburg
Germany

The Common Lisp Cookbook
<http://cl-cookbook.sourceforge.net/>
From: Kent M Pitman
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <sfwhemej8vv.fsf@shell01.TheWorld.com>
···@agharta.de (Dr. Edmund Weitz) writes:

> The more I think about it, the less clear it is to me. My current
> understanding is that my hypothetical function
> WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P is actually
> the same as COMPILED-FUNCTION-P according to the HyperSpec - at least
> as far as I understand the term 'equivalent'[1] there:
> 
> The HyperSpec entry for COMPILE says "If the definition is already a
> compiled function, COMPILE either produces that function itself (i.e.,
> is an identity operation) or an equivalent function." And, according
> to the glossary, a 'compiled function' is an object of type
> COMPILED-FUNCTION, i.e. an object is a compiled function if and only
> if COMPILED-FUNCTION-P applied to this object returns true.

So they'll probably fix it by making EVAL:INTERPRETED-FUNCTION a subclass
of COMPILED-FUNCTION, wouldn't you think?

> [1] I've briefly scanned the introduction of the CLHS to find a
>     definition of the term 'equivalent' but couldn't find it. Maybe if
>     I had found it, that would clarify the situation.

It's hardly surprising it's not there.  But I'd bet it means something
like "computes the same intentional information".  You have to be careful
how you define these things, which is why it doesn't; how might you define
RANDOM such that one implementation's RANDOM was as good as another's,
or that any was even right?

> [2] Note that I'm not interested in proving CMUCL (or any
>     implementation) wrong - I just want to understand this particular
>     topic.

I'd say you're way farther into it already than the number of bits of
precision in the spec on this topic.  It's a matter with some
intentional vaguery and you're pushing against that vaguery pretty
hard, perhaps thinking you'll somehow understand it better by trying
to pretend it's not vague.  You're probably right about the idea that
this should predicate type COMPILED-FUNCTION, but since that's a 
deliberately vague concept, to leave room for implementors to do their
job, that looks like it's about as far as you're likely to go.
From: Pierre R. Mai
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <87g01x3o2w.fsf@orion.bln.pmsf.de>
Kent M Pitman <······@world.std.com> writes:

> ···@agharta.de (Dr. Edmund Weitz) writes:
> 
> > The more I think about it, the less clear it is to me. My current
> > understanding is that my hypothetical function
> > WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P is actually
> > the same as COMPILED-FUNCTION-P according to the HyperSpec - at least
> > as far as I understand the term 'equivalent'[1] there:
> > 
> > The HyperSpec entry for COMPILE says "If the definition is already a
> > compiled function, COMPILE either produces that function itself (i.e.,
> > is an identity operation) or an equivalent function." And, according
> > to the glossary, a 'compiled function' is an object of type
> > COMPILED-FUNCTION, i.e. an object is a compiled function if and only
> > if COMPILED-FUNCTION-P applied to this object returns true.
> 
> So they'll probably fix it by making EVAL:INTERPRETED-FUNCTION a subclass
> of COMPILED-FUNCTION, wouldn't you think?

Actually that is the current situation in CMUCL, where
COMPILED-FUNCTION is defined as FUNCTION, so COMPILED-FUNCTION-P is
just a type-predicate for COMPILED-FUNCTION.

The problem is, that instances of EVAL:INTERPRETED-FUNCTION are not
_necessarily_ fully expanded, as required by the standard.  The
interpreter converts such functions on the fly, and caches their
expansions.  Hence the user can't rely on EVAL:I-Fs being fully
expanded, and therefore, I think EVAL:I-F shouldn't be a subtype
of COMPILED-FUNCTION in CMU CL.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Kent M Pitman
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <sfwr8lhygim.fsf@shell01.TheWorld.com>
"Pierre R. Mai" <····@acm.org> writes:

> Kent M Pitman <······@world.std.com> writes:
...
> > So they'll probably fix it by making EVAL:INTERPRETED-FUNCTION a subclass
> > of COMPILED-FUNCTION, wouldn't you think?
> 
> Actually that is the current situation in CMUCL, where
> COMPILED-FUNCTION is defined as FUNCTION, so COMPILED-FUNCTION-P is
> just a type-predicate for COMPILED-FUNCTION.
> 
> The problem is, that instances of EVAL:INTERPRETED-FUNCTION are not
> _necessarily_ fully expanded, as required by the standard.  The
> interpreter converts such functions on the fly, and caches their
> expansions.  Hence the user can't rely on EVAL:I-Fs being fully
> expanded, and therefore, I think EVAL:I-F shouldn't be a subtype
> of COMPILED-FUNCTION in CMU CL.

Ah, then my personal opinion is that for this reason, not for the original
reason discussed, it shouldn't be calling itself a compiled function...
From: Pierre R. Mai
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <87wuv92209.fsf@orion.bln.pmsf.de>
Kent M Pitman <······@world.std.com> writes:

> "Pierre R. Mai" <····@acm.org> writes:
> 
> > The problem is, that instances of EVAL:INTERPRETED-FUNCTION are not
> > _necessarily_ fully expanded, as required by the standard.  The
> > interpreter converts such functions on the fly, and caches their
> > expansions.  Hence the user can't rely on EVAL:I-Fs being fully
> > expanded, and therefore, I think EVAL:I-F shouldn't be a subtype
> > of COMPILED-FUNCTION in CMU CL.
> 
> Ah, then my personal opinion is that for this reason, not for the original
> reason discussed, it shouldn't be calling itself a compiled function...

Indeed.  I expect that the release after 18d will fix this, though
I'll still have to investigate the issue in more depth...

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Pierre R. Mai
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <87k7ra2aoj.fsf@orion.bln.pmsf.de>
···@agharta.de (Dr. Edmund Weitz) writes:

> The more I think about it, the less clear it is to me. My current
> understanding is that my hypothetical function
> WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P is actually
> the same as COMPILED-FUNCTION-P according to the HyperSpec - at least
> as far as I understand the term 'equivalent'[1] there:
> 
> The HyperSpec entry for COMPILE says "If the definition is already a
> compiled function, COMPILE either produces that function itself (i.e.,
> is an identity operation) or an equivalent function." And, according
> to the glossary, a 'compiled function' is an object of type
> COMPILED-FUNCTION, i.e. an object is a compiled function if and only
> if COMPILED-FUNCTION-P applied to this object returns true.
> 
> So, in my original example, FOO was of type EVAL:INTERPRETED-FUNCTION
> while BAR wasn't, but CMUCL[2] said that FOO was a compiled function,
> and BAR resulted from applying COMPILE to FOO which should yield an
> object 'equivalent' to FOO. I wonder if two objects that are clearly
> not of the same type can be equivalent in this case.

But they are, because both are of type compiled-function, (which is
type-equal to function in CMUCL).

> On the other hand, I agree with you that the usage of global compiler
> declarations renders the implementation of something like
> WILL-ANOTHER-INVOCATION-OF-COMPILE-YIELD-THE-SAME-CODE-P almost
> impossible. But then again, isn't the same true for
> COMPILED-FUNCTION-P already if you follow my arguments above?

Since the standard doesn't define "equivalence" of functions, AFAICT,
we must use "common sense" to interpret the requirement:

I think the language in the standard is clearly intended to allow
implementations to recompile the function.  If that wasn't intended,
they could just have required identity, and been done with it.

Since they wanted to allow the implementation enough leeway to
recompile, they had to loosen the restrictions.  The equivalence
restrictions seems to be just strict enough to require that the
returned function is functionally equivalent to the argument
function, so as not to get the equivalent of #'+ returned from a
compile of #'*...

I don't think the standard requires that an implementation have no
subtypes of compiled-function, so implementations should be allowed to
switch between representations upon recompilation.  If we forbid this,
then implementations would have to provide implementation-defined ways
of offering recompilation, for little gain IMHO.

As it is, compiled-function-p is just (typep x 'compiled-function),
IMHO, nothing more and nothing less.

That still leaves CMU CL's inclusion of EVAL:INTERPRETED-FUNCTION in
COMPILED-FUNCTION illegal, since EVAL:INTERPRETED-FUNCTION objects
don't satisfy the standards minimal requirements for compiled
functions:

<quote entry for type COMPILED-FUNCTION>
Any function may be considered by an implementation to be a a compiled
function if it contains no references to macros that must be expanded
at run time, and it contains no unresolved references to load time
values. See Section 3.2.2 (Compilation Semantics).
</quote>

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Kent M Pitman
Subject: Re: compiled-function-p and CMUCL
Date: 
Message-ID: <sfwy9fpvr7i.fsf@shell01.TheWorld.com>
"Pierre R. Mai" <····@acm.org> writes:

> That still leaves CMU CL's inclusion of EVAL:INTERPRETED-FUNCTION in
> COMPILED-FUNCTION illegal, since EVAL:INTERPRETED-FUNCTION objects
> don't satisfy the standards minimal requirements for compiled
> functions:
> 
> <quote entry for type COMPILED-FUNCTION>
> Any function may be considered by an implementation to be a a compiled
> function if it contains no references to macros that must be expanded
> at run time, and it contains no unresolved references to load time
> values. See Section 3.2.2 (Compilation Semantics).
> </quote>

Not being a CMUCL user, I don't know which of these requirements is
violated by their interpreted function, but am now curious.