From: Kent M Pitman
Subject: Re: *print-readably* and functions
Date: 
Message-ID: <sfw1zybze88.fsf@world.std.com>
Sam Steingold <···@usa.net> writes:

> I just encountered a most unwelcome difference between CL
> implementations I have:
> 
> CLISP (lisp, w32, etc):
> > (let ((*print-readably* t)) (print #'car))
> #.(system::%find-subr 'car) 
> #<system-function car>

Implementations are required to signal an error if they can't do better.
It's permissible to do better, but it's not required.  Solving this
problem "in general" is tough.

> ACL 4.3 (linux)
> [1] USER(4): (let ((*print-readably* t)) (print #'car))   
> 
> Error: Unable to print #<Function CAR> readably and *print-readably* is true.
>   [condition type: print-not-readable]

This behavior is conforming.

> CMU Common Lisp 18a x86-linux 1.4.0
> * (let ((*print-readably* t)) (print #'car))   
> #<Function car {5129EE9}> cannot be printed readably.

If that's an error message and not just a funny print behavior,
this is also conforming.

> Question: I have some structures, some fields of which are functions.
> Is there a way to print them readably?
> Thanks.

What is "readable" is partly caught up in the notion of object identity.
Is EQness of the original function required?  In that case, then only 
named functions can probably win.  (Even then, there's a minor philosophical
question about which version of CAR (or whatever) you should get back if
you've printed the function to a file, then loaded it back into an 
environment that has since "redefined" (e.g., patched a bug) the function.
Is it supposed to remember the "code vector" or the "name"?  Then there
are issues of how much sharing with other objects in the environment 
must be retained.  For example, if a pair of functions is closed over some
data object, and the code vectors are saved, when the result is reloaded,
can it be repatriated?  These questions do not have unique answers.  They
are intended to give you a correct sense that the answer differs 
per-application.  It MIGHT be that there is a reasonable default, and
CLISP apparently takes the premise that there is, but ACL and CMU don't
fail to do that operation necessarily because they weren't bright enough to
think up what CLISP did--they might just feel that this solution is not
adequate in general, or they might even feel that this solution doesn't 
even work for the cases CLISP thinks it does.  All of these implementations
have highly defensible positions.  (See also my Lisp Pointers paper on
equality at http://world.std.com/~pitman/PS/EQUAL.html, which expands
more on the philosophical issues of object identity.)

So what can you do?  Rather than fix functions, fix the printer for your
structures.  That creates enough context that you can figure out the
right solution.  You can write a custom printer and reader and a 
MAKE-LOAD-FORM that will save/restore exactly what you need.  If saving
function names is enough, you can easily write something that arranges
to post-process the structure to turn an object name into an object.

(Another, more low-tech, solution is to observe that a symbol is
funcallable, and just put symbols instead of function objects in those
slots if that suffices for your need.)

From: Bruno Haible
Subject: Re: *print-readably* and functions
Date: 
Message-ID: <69jg7j$ojk$1@nz12.rz.uni-karlsruhe.de>
Kent M Pitman <······@world.std.com> wrote:
>
> What is "readable" is partly caught up in the notion of object identity.
> Is EQness of the original function required?  In that case, then only 
> named functions can probably win.  (Even then, there's a minor philosophical
> question about which version of CAR (or whatever) you should get back if
> you've printed the function to a file, then loaded it back into an 
> environment that has since "redefined" (e.g., patched a bug) the function.
> Is it supposed to remember the "code vector" or the "name"?  Then there
> are issues of how much sharing with other objects in the environment 
> must be retained. ...

All these issues are not an excuse for not doing the simple cases right.

As it stands, if user Steingold has some structures, some fields of which
are functions, he has no way to print them readably. This is annoying.

I propose that ANSI CL be amended as follows:

  1. All built-in functions (i.e. those defined by ANSI CL itself) can
     be printed readably.

  2. All user-defined functions with a null lexical environment (this is
     a concession to GCL, which - I think - has problems with non-null
     lexical environments) can be printed readably.

When such a printed function is read using READ, it shall behave the
same way as if it had been written out using COMPILE-FILE and then LOADed.

Cost to implementors: Small. Dumping out compiled code is already
implemented as part of the compilers, and dumping out interpreted code
in a null lexical environment is nothing more than a PPRINT.

CLISP actually can print readably functions with a non-null lexical
environment, such as David Gabois' example:

> (let ((x 0))
    (locally (declare (compile))
      (list #'(lambda () (incf x))
            #'(lambda () (incf x)))))
(#<COMPILED-CLOSURE #:COMPILED-FORM-55-1>
  #<COMPILED-CLOSURE #:COMPILED-FORM-55-2>
)

> (write * :readably t)
(#Y(#:COMPILED-FORM-55-1
     #21Y(00 00 00 00 00 00 00 00 00 01 D8 C4 5E 14 71 99 D8 C4 5F 19 01)
     #1=#(X 0. NIL) 1.
   )
  #Y(#:COMPILED-FORM-55-2
     #21Y(00 00 00 00 00 00 00 00 00 01 D8 C4 5E 14 71 99 D8 C4 5F 19 01) #1#
     1.
)   )

and I assume that the FASL writer of other implementations have similar
capabilities.

Now about the EQness issue: Testing function identity for EQ is always a
bad thing because then your code stops working when you switch on the tracer
or profiler. The EQness of the lexical environment, if some implementation
decides to handles these functions too, can not be guaranteed. Just the
same way as when printing an uninterned symbol, its READ result will
certainly be different from the different symbol. And we don't really
worry about this latter case. So why should we worry about the EQness
of functions' lexical environment?

> So what can you do?  Rather than fix functions, fix the printer for your
> structures.  That creates enough context that you can figure out the
> right solution.  You can write a custom printer and reader and a 
> MAKE-LOAD-FORM that will save/restore exactly what you need.

This is not the right answer. What if a user wants so write out lists which
contain functions, or vectors which contain functions?

MAKE-LOAD-FORM is programmable by the user, for data structures introduced
by the user. Functions are totally under control of the implementation,
and therefore it is the implementation's duty to provide a way to print
them readably.

                Bruno Haible
                <······@clisp.cons.org>
From: Kent M Pitman
Subject: Re: *print-readably* and functions
Date: 
Message-ID: <sfwzpky1n8q.fsf@world.std.com>
······@ilog.fr (Bruno Haible) writes:

> Kent M Pitman <······@world.std.com> wrote:
> >
> > What is "readable" is partly caught up in the notion of object identity.[...]
>
> All these issues are not an excuse for not doing the simple cases right.

Excuse me but there is no uniquely determined case of right, so I'm sorry to
say they are an "excuse".   As a matter of personal preference, I happen not 
agree that your interpretation is correct, by the way, because it does NOT
preserve object identity.

To use an English expression I once heard used in a Lisp course long ago
(I think by Bernie Greenberg), "You can change your socks or you can change
your socks."  That is, the phrase "you can change your socks" is ambiguous
(in English).  You can change them by MODIFYING them (e.g., embroidering your
initials into them) or you can change them by REPLACING them (putting on a 
new pair).  In my model of the universe, the definition of object identity
is that if you have two objects and you wonder whether they are identical,
one thing you can do is cut one and see if the other bleeds.  If I have
defined a system function, and I am a system maintainer, then 
 (defun open ...)
IS permissible to patch the system.  But in doing this, I am REPLACING
the definition, I am not MODIFYING the definition.  So if I dump an object
as #.(function-named 'open) and then later redefine it and then later
load it back, getting me the object in #'OPEN does NOT match the behavior
of another data flow path which has held onto #'OPEN since BEFORE the 
replacement.  As a consequence, identity has been violated.  This is  
simply not a simple matter where there is an OBVIOUS answer.  What is
acceptable and what is not is application dependent, and applications
are fully empowered to do as close to the right thing as they can.  But
when the system tries to do too much to them, it does not encourage the
user to consider the very complex philosophical issues (such as "sufficiency
of identity") and it instead encourages them to think there is only one
possible point of view (as you presumably do if you think there is an "obvious"
answer--perhaps even because you've now lived with you answer and you like it
personally enough that you are not willing to exclude other people's models
of the world as uninteresting to you ... however, such willingness to
exclude other's models does not make those models go away, it just makes
people frustrated with your lack of caring about their models).

It might appear intolerant for me to take the position of rejecting
your position, but I am not proposing my position as an alternative.
I am merely noting there are competing positions and saying that in
such cases, it is often better to leave it to users because users'
needs differ and in leaving it to users, you leave them the best
chance of getting it right.  Just like religion--in my country, a
majority of people probably think religion matters a lot, but just
about no one agrees on the details.  So we don't define a national
religion exactly because it is so important a thing that we feel it is
best left to the individual.

> As it stands, if user Steingold has some structures, some fields of which
> are functions, he has no way to print them readably. This is annoying.
 
There is no uniquely determined meaning to printing them readably.
That is annoying, too.  Life is tough.

> I propose that ANSI CL be amended as follows:
> 
>   1. All built-in functions (i.e. those defined by ANSI CL itself) can
>      be printed readably.

This isn't the forum for making such a proposal.  ANSI has a process
for making such proposals even when no process is engaged but I don't
know what the mechanism is.  OR you could wait until there is a
process in place and then get your representative to the ANSI process
(perhaps you yourself, if you decide to be a member of the committee
designing CL) to raise your proposal.

>   2. All user-defined functions with a null lexical environment (this is
>      a concession to GCL, which - I think - has problems with non-null
>      lexical environments) can be printed readably.

This second one loses object identity even when the original function
has NOT been redefined.  Ick.  That's even worse.  It would also be
inconsistent conceptually with your proposal 1.  It would be better
just to say any named function should be possible to recapture by mentioning
its name.  Why you don't just appeal to #.#'foo escapes me, btw.  
 
> When such a printed function is read using READ, it shall behave the
> same way as if it had been written out using COMPILE-FILE and then LOADed.
> 
> Cost to implementors: Small. Dumping out compiled code is already
> implemented as part of the compilers, and dumping out interpreted code
> in a null lexical environment is nothing more than a PPRINT.

Yes, the cost to implementors of forcing a potentially wrong view on users is
small.
 
> Now about the EQness issue: Testing function identity for EQ is always a
> bad thing because then your code stops working when you switch on the tracer
> or profiler.

This presumes a certain implementation of the tracer or profiler.  It
also presumes that your code is not savvy about tracing encapsulation;
if your code understands what encapsulation is, it will be surprised
if tracing doesn't change identity.  Identity cuts both ways.


>The EQness of the lexical environment, if some implementation
> decides to handles these functions too, can not be guaranteed. Just the
> same way as when printing an uninterned symbol, its READ result will
> certainly be different from the different symbol. And we don't really
> worry about this latter case. So why should we worry about the EQness
> of functions' lexical environment?

Using an alternate theory, involving consistent name access, that neither
you nor I is advocating, lexical closure issues are not relevant since 
using names, you can recapture things even when they were lexically closed.
Again, there are multiple ways to look at this.  Why force one?


> > So what can you do?  Rather than fix functions, fix the printer for your
> > structures.  That creates enough context that you can figure out the
> > right solution.  You can write a custom printer and reader and a 
> > MAKE-LOAD-FORM that will save/restore exactly what you need.
> 
> This is not the right answer. What if a user wants so write out lists which
> contain functions, or vectors which contain functions?

One can write a printer that does this.  Lists and vectors are just lightweight
data structures.  In situations with complex semantics (and the identity of a
function is complex), one has to resort to complex means.  Simplifying the 
problem by pretending half of it doens't exist sometimes works (when the
other half is null) and sometimes loses badly.
 
> MAKE-LOAD-FORM is programmable by the user, for data structures introduced
> by the user. Functions are totally under control of the implementation,
> and therefore it is the implementation's duty to provide a way to print
> them readably.

Or not to provide such a way.
From: Bruno Haible
Subject: *print-readably* and functions (II)
Date: 
Message-ID: <69okf3$k5i$1@nz12.rz.uni-karlsruhe.de>
Here is a summary of my arguments, philosophical and practical.

1. Currently, according to ANSI CL, functions cannot be relied on being
printable when *print-readably* is true. This is annoying because
functions are first class objects in all other respects, and users
(as well as implementors) want to be able to save structures, lists
or vectors containing functions.

It has been proposed that users use MAKE-LOAD-FORM for this purpose.
This would imply that users write a method for
               MAKE-LOAD-FORM ((object FUNCTION))
No two software packages in a system can do this without causing
conflicts. Therefore, if MAKE-LOAD-FORM is to be used for this purpose,
it is the implementation's duty to provide the method.

2. The relation we can expect to hold between

                 object

and

          (read-from-string (prin1-to-string object))

varies according to the type of object:

  - For interned symbols, it is EQ.
  - For numbers and characters, it is EQL.
  - For lists, it is EQUAL.
  - For vectors, it is EQUALP.
  - For uninterned symbols, it is neither of these.

Therefore there is no reason whatsoever to expect that for functions
this relation shall be EQ. In fact, the expected relation for functions
can be formulated as this:

   If for any world state S and any arguments A, function fn1 terminates
   iff fn2 terminates, and - in case of termination - the values of fn1
   and fn2 are the same, and the side effects of fn1 on S would be the same
   as the side effects of fn2 on S, then we consider fn1 and fn2 as
   equivalent.

This is a well-defined equivalence relation, but it is not computable
(because an implementation of this relation would solve the halting
problem).

However, it is possible to _guarantee_ a priori the equivalence of
two functions, for example if fn1 and fn2 are two COMPILE results of
the same interpreted function, maybe with different optimization settings.
In effect, this guarantee is usually implemented through the fasl file
dumper.
It is not possible to write an _algorithm_ to _test_ this equivalence
a posteriori, that's all.

3. Therefore I propose to amend ANSI CL, in the next round of amendments,
to the effect:

     Built-in functions and user-defined functions defined in a null
     lexical environment can be printed when *print-readably* /= NIL,
     and READing in the result produces a function which is equivalent
     (in the sense above) to the original function.

Note that this shall hold for named and unnamed functions equally.

                 Bruno Haible
                 <······@clisp.cons.org>
From: Tim Bradshaw
Subject: Re: *print-readably* and functions (II)
Date: 
Message-ID: <ey390sc7nya.fsf@todday.aiai.ed.ac.uk>
* Bruno Haible wrote:

> 1. Currently, according to ANSI CL, functions cannot be relied on being
> printable when *print-readably* is true. This is annoying because
> functions are first class objects in all other respects, and users
> (as well as implementors) want to be able to save structures, lists
> or vectors containing functions.

first class does not imply `printable readably', why should it be?  If
you want functions to print readably there are a googol other things
that are equally important -- hash tables, for instance, or CLOS
objects.

It seems to me that the real problem (if there is one) is that there
isn't a mechanism which lets you *write* a function printer, whereas
for hashtables there is.  In that case, surely the right thing to fix
would be to change FUNCTION-LAMBDA-EXPRESSION's contract somewhat so
you can guarantee that it gives something useful.

Now, how do you deal with generic functions?

--tim