[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

synonym streams



   From: Tim Converse <converse@gargoyle.uchicago.edu>

	   Does a synonym stream have all the rights and duties of a
   normal stream?  In particular, can you create a synonym stream for
   a synonym stream?

	   In practice, it's seeming as though the latter does not work.
   I tried to create a synonym stream for *standard-output* (forgetting
   that it itself is a synonym for *terminal-io*).  E.G.

	   (setq *memory-output* (make-synonym-stream '*standard-output*))
	   (princ "stuff" *memory-output*)

   and found that the print statement hangs (Allegro CL 3.1.4 [Sun4]).  The
   above works fine when *memory-output* is defined as a synonym for
   *terminal-io*.

Synonym stream chains of any length do work, but circularity causes
infinite recursion.  You would certainly expect this to fail:

	(setq *foo* (make-synonym-stream '*foo*))
	(princ "foo" *foo)

Now consider that a plausible implementation of princ is:

	(defun princ (x &optional (*standard-output* *standard-output*))
	  ...)

This definition is a simplified version of what Allero 3.1 does.  It
is unclear whether this implementation is precluded by anything in the
CLtL.  In any case, it is impossible for two CLtL-conforming program
modules to both use synonym streams and be guaranteed of avoiding such
circularity without knowing about what variables the other binds
internally.  In this case, the Allegro 3.1 output system is one of
those modules.  One could implement circularity checking at some
runtime cost, but the circularity would still be an error, and
presumably not whatthe programmer intended.

[Personal opinion: This is only one reason why synonym streams are a
botched design.  Tying stream indirection to special variable binding
was a semantic muddle.]

A better implementation would be for no program module ever to bind a
public stream variable that might be bound by some other
non-cooperating module.  Of course, this restriction also reduces
functionality so might not be acceptable.  Whether or not Alegro's
implementation is legal, it's fair to say that it would be better if
Allegro did not bind any stream variables internally but instead
passed stream arguments to internal functions as explicit arguments.

To solve your immediate problem you need some knowledge about what
Allegro does and doesn't do internally.  Since the Allegro "prgram
module" indeed does bind *standard-output* and *standard-input*
internally, it is not safe for your "program module" to make synonym
streams for these two special variables.  I don't think Allegro ever
dynamically binds any of the other lisp-package stream variables, so
you can safely make synonym streams for them.  So it might be that a
sufficient solution for you is to make synonyms for *terminal-io*
rather than *standard-output*.

You might be interested to know that the printer implementation in
Allegro 4.0 attempts never to bind *standard-output* internally.  The
stream and I/O system of Allegro 4.0 is nearly a complete rewrite in
order to integrate the new X3J13 pretty printer and to reimplement
streams as CLOS classes.  This makes user-customizable streams at last
a reasonable possibility.  X3J13 did not adopt this change because it
was felt to be too large a change to _require_ so late in the
standards process, but people generally felt it was a technologically
sound idea.  (To the extent that CLOSified streams are a good idea,
synonym streams and the other types of indirect streams should be
further deprecated.  Stream generic functions discriminate on the type
of their stream arguments, but a synonym stream essentially hides from
a discriminating function everything useful about its target stream.