From: ········@gmail.com
Subject: Unreadable-object-p?
Date: 
Message-ID: <1176893319.533808.140950@d57g2000hsg.googlegroups.com>
Hi--

Does CL have a predicate for determining whether the printed
representation of
an object is readable or not, given the default readtable, and if not,
is it easy
to implement one?

For example:

(unreadable-object-p (make-hash-table))
T

(unreadable-object-p #(1 2 3 (4 5))
NIL

Tiarnán

From: Timofei Shatrov
Subject: Re: Unreadable-object-p?
Date: 
Message-ID: <462606b5.16322770@news.readfreenews.net>
On 18 Apr 2007 03:48:39 -0700, ·········@gmail.com" <········@gmail.com> tried
to confuse everyone with this message:

>Hi--
>
>Does CL have a predicate for determining whether the printed
>representation of
>an object is readable or not, given the default readtable, and if not,
>is it easy
>to implement one?
>
>For example:
>
>(unreadable-object-p (make-hash-table))
>T
>
>(unreadable-object-p #(1 2 3 (4 5))
>NIL
>

(defun unreadable-object-p (obj)
 (handler-case (progn (read-from-string (format nil "~s~%" obj)) nil)
   (error () t)))

-- 
|Don't believe this - you're not worthless              ,gr---------.ru
|It's us against millions and we can't take them all... |  ue     il   |
|But we can take them on!                               |     @ma      |
|                       (A Wilhelm Scream - The Rip)    |______________|
From: Kent M Pitman
Subject: Re: Unreadable-object-p?
Date: 
Message-ID: <uzm55datm.fsf@nhplace.com>
····@mail.ru (Timofei Shatrov) writes:

> On 18 Apr 2007 03:48:39 -0700, ·········@gmail.com" <········@gmail.com>
> tried to confuse everyone with this message:
>
> >Hi--
> >
> >Does CL have a predicate for determining whether the printed
> >representation of
> >an object is readable or not, given the default readtable, and if not,
> >is it easy
> >to implement one?
> >
> >For example:
> >
> >(unreadable-object-p (make-hash-table))
> >T
> >
> >(unreadable-object-p #(1 2 3 (4 5))
> >NIL
> 
> (defun unreadable-object-p (obj)
>  (handler-case (progn (read-from-string (format nil "~s~%" obj)) nil)
>    (error () t)))

There are two slight problems with this:

 * Re-reading the object potentially has side-effects.
   (For that matter, so does printing it.)
   One of those side-effects might be heaps (pardon the pun) of useless
   consing, incidentally.

 * The object may print differently to different streams, such that
   answering the question without a stream argument is hard.
      
The hard part is that if you DO get the stream argument, then there is
no unprint, so you have to print to the ACTUAL stream to find out if you
can print to it.  You can't cheat.  There is no MAKE-SIMILAR-STREAM
operator.  Consequently, just as with the study of the Halting Problem,
you can construct pairs of streams and print methods that are intent
on frustrating you...

   (defvar *some-stream* (open "some-particular-file"
                               :direction :output
                               :if-exists :error))

   (defmethod print-object ((x foo) stream)
     (if *some-stream* 
         (format stream ". . . bad stuff . . .")
         (format stream "...goodstuff...")))

Even without devious programs like this, the problem is that some print
methods may really work differently on streams that are buffered, streams
that have a particular stream element type, etc.  So the write might go
differently (and hence the the later read) on some streams and not others.

So, were this not so, and output to any stream was as good as to any other,
you might think of instead just writing this, which only does the PRINT
and not the READ...

  (defun unreadable-object-p (obj)
    (let ((null-output-stream (make-broadcast-stream))
          (*print-readably* t))
      (handler-case (prin1 obj null-output-stream)
        (error (condition) t)
        (:no-error (value) nil))))

  (unreadable-object-p #(a b c)) => NIL
  (unreadable-object-p #'car) => T

(Personally, if I _were_ writing such a function, and it had any possibility
 of general purpose use, I'd make it have a positive name, not a negative
 one.  Make it be READABLE-OBJECT-P and wrap NOT around a call to it if
 you need it.)

IMO, the normal way the language intends you to deal with this is to
simply assume (that is, require) everything has a valid print method
that allows it to print readably, and then to bind *PRINT-READABLY* to
T and then to make sure all your user-defined print methods signal an
error if they don't think they can live up to your expectations. This
doesn't allow you to ask in advance, but it allows you to not walk
away from an application thinking you've successfully saved its data,
for example.

And this is best anyway, because philosophically, you can't know if
I/O will succeed unless you try.  You can't assume a host won't go down,
a file won't exceed disk space, an array won't overrun the memory size
limit.  And so you must be prepared, at least emotionally if not also
programmatically, for a write operation to fail anyway.  And once you 
are, preparing for the errors that *PRINT-READABLY* signals is no big deal.

Note: I wrote this hastily and tested it very little, so apologies if 
 I made any typos.  Hope it gets the general idea across.

Total aside to ····@mail.ru: I'd lose the "tried to confuse everyone"
 opening in your message reply thing.  It probably means to be cute, 
 and wasn't probably custom-crafted for this particular reply,
 but it risks offending people unintentionally exactly by its likely
 inattention to context.  We're a strong-headed enough bunch that extra,
 unnecessary provocation probably doesn't serve us.
From: Richard M Kreuter
Subject: Re: Unreadable-object-p?
Date: 
Message-ID: <87abx5fs39.fsf@tan-ru.localdomain>
Kent M Pitman <······@nhplace.com> writes:
>> On 18 Apr 2007 03:48:39 -0700, ·········@gmail.com" <········@gmail.com>
>> >
>> >Does CL have a predicate for determining whether the printed
>> >representation of an object is readable or not, given the default
>> >readtable, and if not, is it easy to implement one?
<snip>
> IMO, the normal way the language intends you to deal with this is to
> simply assume (that is, require) everything has a valid print method
> that allows it to print readably, and then to bind *PRINT-READABLY*
> to T and then to make sure all your user-defined print methods
> signal an error if they don't think they can live up to your
> expectations. This doesn't allow you to ask in advance, but it
> allows you to not walk away from an application thinking you've
> successfully saved its data, for example.
>
> And this is best anyway, because philosophically, you can't know if
> I/O will succeed unless you try.  You can't assume a host won't go
> down, a file won't exceed disk space, an array won't overrun the
> memory size limit.  And so you must be prepared, at least
> emotionally if not also programmatically, for a write operation to
> fail anyway.  And once you are, preparing for the errors that
> *PRINT-READABLY* signals is no big deal.

Wouldn't printing to a broadcast stream with no output streams solve
the OP's problem without also requiring the proposed predicate to be
prepared to handle I/O failures?

(defun readable-object-p (object)
  (multiple-value-bind (return condition)
      (ignore-errors
	(let ((null (make-broadcast-stream)))
	  (let ((*print-readably* t))
	    (print object null))))
    (declare (ignore return))
    (if condition
	nil
	t)))

--
RmK
From: Kent M Pitman
Subject: Re: Unreadable-object-p?
Date: 
Message-ID: <uodlduuyz.fsf@nhplace.com>
Richard M Kreuter <·······@progn.net> writes:

> Kent M Pitman <······@nhplace.com> writes:
> >> On 18 Apr 2007 03:48:39 -0700, ·········@gmail.com" <········@gmail.com>
> >> >
> >> >Does CL have a predicate for determining whether the printed
> >> >representation of an object is readable or not, given the default
> >> >readtable, and if not, is it easy to implement one?
> <snip>
> > IMO, the normal way the language intends you to deal with this is to
> > simply assume (that is, require) everything has a valid print method
> > that allows it to print readably, and then to bind *PRINT-READABLY*
> > to T and then to make sure all your user-defined print methods
> > signal an error if they don't think they can live up to your
> > expectations. This doesn't allow you to ask in advance, but it
> > allows you to not walk away from an application thinking you've
> > successfully saved its data, for example.
> >
> > And this is best anyway, because philosophically, you can't know if
> > I/O will succeed unless you try.  You can't assume a host won't go
> > down, a file won't exceed disk space, an array won't overrun the
> > memory size limit.  And so you must be prepared, at least
> > emotionally if not also programmatically, for a write operation to
> > fail anyway.  And once you are, preparing for the errors that
> > *PRINT-READABLY* signals is no big deal.
> 
> Wouldn't printing to a broadcast stream with no output streams solve
> the OP's problem without also requiring the proposed predicate to be
> prepared to handle I/O failures?
> 
> (defun readable-object-p (object)
>   (multiple-value-bind (return condition)
>       (ignore-errors
> 	(let ((null (make-broadcast-stream)))
> 	  (let ((*print-readably* t))
> 	    (print object null))))
>     (declare (ignore return))
>     (if condition
> 	nil
> 	t)))

Well, this is what I proposed in the message you're replying to
when I wrote this, which is pretty much the same, except that the "un-"
part reverses the T/NIL result value.

  (defun unreadable-object-p (obj)
    (let ((null-output-stream (make-broadcast-stream))
          (*print-readably* t))
      (handler-case (prin1 obj null-output-stream)
        (error (condition) t)
        (:no-error (value) nil))))

But the problem with both your suggestion and mine, which I had tried to
explain, is that it's theoretically possible for a print method to do 
something different depending on the stream, so you can't print to a stream
other than the one you plan to really output to and be sure it will work
to output to that stream.  Will it _probably_ work?  Yeah.  It's often good
to make reasonable simplifying assumptions.  But it's more risky to make
them without knowing you are doing so and being able to assess whether you
like the risk.