I was trying to figure out what synonym streams are for and came up with the following explanation. I'm hoping someone can tell me if this is more or less right.
Suppose I want to make an encapsulated stream (say an echo stream) that will track changes to a well-known stream variable such as *standard-input*. I can make it like this:
Now if someone changes or rebinds *standard-output* while I'm still using the echo stream, it will pick up on the change. Whereas if I made the echo stream like:
it would keep using the stream referenced by *standard-input* at the time the echo stream was made, even if *standard-input* is later reset or rebound. A similar situation could arise when making a broadcast stream.
Is that it? Or is there some other use that I'm totally missing?
Peter Seibel <pe...@javamonkey.com> writes: > I was trying to figure out what synonym streams are for and came up > with the following explanation. I'm hoping someone can tell me if this > is more or less right.
> Suppose I want to make an encapsulated stream (say an echo stream) > that will track changes to a well-known stream variable such as > *standard-input*. I can make it like this:
> Now if someone changes or rebinds *standard-output* while I'm still > using the echo stream, it will pick up on the change. Whereas if I > made the echo stream like:
> it would keep using the stream referenced by *standard-input* at the > time the echo stream was made, even if *standard-input* is later reset > or rebound. A similar situation could arise when making a broadcast > stream.
> Is that it? Or is there some other use that I'm totally missing?
No. I'm not even sure you're allowed to do this (Because of potential circularities).
It's so you can have interdependent "well-known variables". In particular, it allows *standard-input*, *standard-output*, *query-io*, and *debug-io* to be synonym streams to *terminal-io* without having to rebind anything other than *terminal-io* to change the "focus of attention" to another window. On the Lisp Machine, *terminal-io* (which was very badly named), meant what might better have been called *current-window*.
Unfortunately, because hte hygiene for maintaining window focus is ill-specified in CL in general, being left to implementations, you mostly can't use it in this way. You could, of course, make similar mechanisms of your own, so that you only had to bind one focus variable in order to change things.
Note that LispWorks, for example, just makes *terminal-io* go to some tty-like window that I, for one, never want to talk to. I think they decided (alas) that this icky DOS-like window was the "terminal". The window more resembles what the Lisp Machine would call the "cold load stream", the court of last resort in debugging. The intended abstraction, I think, by those who created this set of names, would have been that they would have let that be *terminal-io* until they had bootstrapped their way into multiprocessing, and then would have re-bound *terminal-io* per process, leaving *standard-input* and friends alone (because they'd be synonym streams). By the time I found what they'd one (which is not non-conforming, as far as I know, just not what I and others intended), I'm not sure there was any way to get them to back out of it.
Nor are they likely to be the only ones who did this. I think there are other implementations that have said that there is always a *standard-input* and *standard-output*, like stdin and stdout, but that sometimes there is no (or no useful) *terminal-io* if windowing operaitons of some class are not supported. THis is again, not what I think would have been intended, but the spec is so vague that there's no real way to prevent it.
[Historically, Maclisp had TYI ("tty input") and TYO ("tty output") which were really both *terminal-io* and were very definitely not anything you'd re-bind, mostly because the mechanisms for it were terrible and mostly things would break. Everything relied on these really holding a console, not just a stream, and being able to do console-type IO to it. People would sometimes try and then make a mess of things by getting them inconsistent (one of TYO bound to a stream and one to the terminal), or worse, they'd get one bound to one part of the screen and one part of another (which was a neat trick since the operating systems Maclisp ran under didn't really have "windows" in the modern sense, but sometimes still could fake it by giving you lines n-m of the screen as if it were a sort of window ,so you could write TALK programs or so that you could reserve a line at the bottom as a status line that other text didn't write over.) There was a (status ttycons), a special form on each stream, that could be changed by (sstatus ttycons ...) that would help you know what to rebind TYI or TYO to if you undrestood all of this and really wanted to rebind them to another value... there was nothing like the CL spec forcing you to not do things. The maclisp manual mostly tried to excavate "current practice" and tell people how not to get hurt in what was really a wild, wild west kind of environment that changed all the time in the way everyone here is always complaining that ANSI CL doesn't... every monday (or so) an email saying what had changed and asking you to please update your programs. .. anyway, so TYI and TYO still had to be a screen stream if you didn't want the aforementinoed big mess. Maclisp's IO system underwent an upheaval at some point just before I came on the scene, and it continued to support both "old i/o" and "new i/o" even after the upgrade. Old-IO really had no multiple streams, but had a way of changing the reader with no args (or with stream arg NIL) to read from an alternate place under control of some variables with caret in their names (^R, ^W, etc.) that corresponded to the interactive interrupt character you could do to affect whether I/O was coming from such a stream. (The interrupt character would just set or unset these variables.) When New-IO came along, creating real "streams" you could open, pepole preferred using those, but there was no way to talk about *standard-output* because it was nowhere in the abstraction. The right thing would have been contniued use of old-io's NIL stream but (a) having it be part of old-io was daunting, and (b) it really did have those awful enabling variables that were a mess and no one wanted to use. I mentino all of this not to teach anyone how old-io or new-io worked in Maclisp, but rather to say that if you think synonym streams are not very elegant, you have to see where people were historically digging themselves out from, because they were a MASSIVE improvement over prior abstractions, even if they don't offer much today.]
> > I was trying to figure out what synonym streams are for and came up > > with the following explanation. I'm hoping someone can tell me if this > > is more or less right.
> > Suppose I want to make an encapsulated stream (say an echo stream) > > that will track changes to a well-known stream variable such as > > *standard-input*. I can make it like this:
> > Now if someone changes or rebinds *standard-output* while I'm still > > using the echo stream, it will pick up on the change. Whereas if I > > made the echo stream like:
> > it would keep using the stream referenced by *standard-input* at the > > time the echo stream was made, even if *standard-input* is later reset > > or rebound. A similar situation could arise when making a broadcast > > stream.
> > Is that it? Or is there some other use that I'm totally missing?
> No. I'm not even sure you're allowed to do this (Because of potential > circularities).
Oh well, so much for my latest effort at creative spec interpretation and reverse engineering. Thanks for setting me straight.
> It's so you can have interdependent "well-known variables". In > particular, it allows *standard-input*, *standard-output*, > *query-io*, and *debug-io* to be synonym streams to *terminal-io* > without having to rebind anything other than *terminal-io* to change > the "focus of attention" to another window. On the Lisp Machine, > *terminal-io* (which was very badly named), meant what might better > have been called *current-window*.
Ah. That makes a ton of sense. I should have figured that out. Actually, I did go exploring a little ways down that path but in Allegro 6.2 none of those variables reference synonym streams though some of them seem to reference the same terminal-simple-stream. But that fits with what you say late--that this mechanism is perhaps not widely used any more. Thanks again.
In article <sfwu17krysc....@shell01.TheWorld.com>, Kent M Pitman wrote: > The maclisp manual mostly tried to excavate "current practice" and > tell people how not to get hurt in what was really a wild, wild west > kind of environment that changed all the time in the way everyone > here is always complaining that ANSI CL doesn't... every monday (or > so) an email saying what had changed and asking you to please update > your programs.
This one gave me a chill when fortune first spit it out:
> === ALL USERS PLEASE NOTE ========================
> The garbage collector now works. In addition a new, experimental > garbage collection algorithm has been installed. With > SI:%DSK-GC-QLX-BITS set to 17, (NOT the default) the old garbage > collection algorithm remains in force; when virtual storage is > filled, the machine cold boots itself. With SI:%DSK-GC-QLX-BITS set > to 23, the new garbage collector is enabled. Unlike most garbage > collectors, the new gc starts its mark phase from the mind of the > user, rather than from the obarray. This allows the garbage > collection of significantly more Qs. As the garbage collector runs, > it may ask you something like "Do you remember what SI:RDTBL-TRANS > does?", and if you can't give a reasonable answer in thirty seconds, > the symbol becomes a candidate for GCing. The variable > SI:%GC-QLX-LUSER-TM governs how long the GC waits before timing out > the user.
Certainly this one's tongue-in-cheek, but did you ever get similarly broad changes? "QUOTE now works." "LIST is broken." "We accidentally bound DEFUN to REBOOT; sorry."
[The story you quoted was a LispM story, not a Maclisp story, btw. Not that it wasn't just as fun there. But Maclisp had no packages, and it had only a mark/sweep GC since it had only 1.25MB of storage (well, to be precise, 256KW of 36-bit words (sometimes seen as 4 9-bit bytes, though it was not a byte-coded machine generally; as strings you got 5 7-bit bytes with 1 bit left over to use for "fun" purposes)) with usually 3-6 times that much real memory (so the running job and even many stopped jobs were fully paged in), and there was no performance issue with mark/sweep as there were in later machines that had more virtual memory than real memory...
Nevertheless, I'll interpret your question as one about Maclisp, circa 1979-1980, just before CL, since was what I was talking about.]
> Certainly this one's tongue-in-cheek, but did you ever get similarly > broad changes? "QUOTE now works." "LIST is broken." "We > accidentally bound DEFUN to REBOOT; sorry."
Sure. Especially with new stuff. My favorite was when we installed backquote for the first time. There were several competing versions and the first one installed always did a full copy. I excitedly changed a ton of my calls to CONS, LIST, etc. into backquote forms. Then a couple weeks later, there was an announcement saying it was "too expensive" for it to be copying all the time, and it would only copy structure it had to. Great for folks just working from templates and not planning to side-effect, but my calls to CONS, LIST, etc. were there for a reason--to get fresh structure. It was a pain to go back and change things. But at least I could assume that basically all calls to backquote were suspect, since the macro had not existed for a long time...
Plus people were always finding bugs and sometimes when JonL White couldn't fix them, he'd just put in a breakpoint acknowledging the problem and who had reported it, so you'd be running along and see ;BKPT PSZ LOSES or some such thing, indicating that there was work remaining to do in coding something you'd thought was fully working...
It was fun (when it was) mostly because of two things: (1) we didn't distribute the code, we mostly used it locally and all our users dialed in, so when we needed a fix, installing it "worldwide" was not that hard and (2) our livelihood did not depend upon its correctness. we weren't commercial, we were sponsored research staff.
But after a while I got tired of being told my code had changed meaning and that it was my responsibility to update it. My code meant what I meant it to mean, even when broken by changes, and that little joke about how it had changed meaning and to please "update my program" wore thin after a while...
It was frustrating enough that I cringe just a little when people romanticize overly about how great it was when things used to change faster. ANSI CL may not have changed in a long time, but that was its whole purpose in life. Many people, including (D)ARPA -- one of our big sponsors, had told us that Maclisp was too unstable a platform to build long-term products upon. And they were right.
In article <sfw7k4fm5on....@shell01.TheWorld.com>, Kent M Pitman
<pit...@world.std.com> wrote: > It was frustrating enough that I cringe just a little when people > romanticize overly about how great it was when things used to change > faster. ANSI CL may not have changed in a long time, but that was its > whole purpose in life. Many people, including (D)ARPA -- one of our > big sponsors, had told us that Maclisp was too unstable a platform to > build long-term products upon. And they were right.
At the risk of opening myself up to a beating when the bruises from the last one haven't completely healed up yet I would like to say that while I am sympathetic to Kent's point, I also believe that the opposite extreme - no change at all - is also harmful. Change brings problems, no doubt. But the solution IMO is not to eliminate change, but to manage it better.
I have recently developed a text only version of Othello. (Basically a case study of applying a alpha-Beta pruning search algorithm.) Now I would like to extend this program to be an othello server. The client interface will be a Java applet. Communication I suspect will use XML-RPC 1.0 . Does anyone have experience with this? What tools should I use? I use Corman Lisp.
Peter Seibel wrote: > I was trying to figure out what synonym streams are for and came up > with the following explanation. I'm hoping someone can tell me if this > is more or less right. > ... > Is that it? Or is there some other use that I'm totally missing?
Since I know you are writing about Common Lisp, how about this prose:
The idea of the synonym-stream was an attempt to provide a layer of stream indirection and a mechanism capable of rapid lambda binding of stream capability depending on, well, lambda binding. It was and remains a horrible `kludge' (q.v.) because it make streams, if not fully second-class objects, something less than first class. The important aspects of the identity of a synonym stream are not discernable by eq, and the important aspects of its behavior are not discernable by class-of or type-of, and do not even remain constant within a single function body.
Synonym streams should be deprecated, not because they might not be useful in overly-clever code, but because their existence stands in the way of other, more-useful and more-extensible stream paradigms that are more compatible with the proven coding abstractions in the language.
> Peter Seibel wrote: > > I was trying to figure out what synonym streams are for and came up > > with the following explanation. I'm hoping someone can tell me if this > > is more or less right. > > ... > > Is that it? Or is there some other use that I'm totally missing?
> Since I know you are writing about Common Lisp, how about this prose:
> The idea of the synonym-stream was an attempt to provide a layer of > stream indirection and a mechanism capable of rapid lambda binding > of stream capability depending on, well, lambda binding. It was and > remains a horrible `kludge' (q.v.) because it make streams, if not > fully second-class objects, something less than first class. The > important aspects of the identity of a synonym stream are not > discernable by eq, and the important aspects of its behavior are not > discernable by class-of or type-of, and do not even remain constant > within a single function body.
> Synonym streams should be deprecated, not because they might not be > useful in overly-clever code, but because their existence stands in > the way of other, more-useful and more-extensible stream paradigms > that are more compatible with the proven coding abstractions in the > language.
Thanks. With the perspective Mr. Pitman and you have given me on this issue, I suspect I'll probably just not bring it up unless I happen to have an appendix that says *something* about every class in the COMMON-LISP package.
Steven M. Haflich writes: > Synonym streams should be deprecated, not because they might not be > useful in overly-clever code, but because their existence stands in > the way of other, more-useful and more-extensible stream paradigms > that are more compatible with the proven coding abstractions in the > language.
>>Synonym streams should be deprecated, not because they might not be >>useful in overly-clever code, but because their existence stands in >>the way of other, more-useful and more-extensible stream paradigms >>that are more compatible with the proven coding abstractions in the >>language.
> Which paradigms?
Gray Streams, Simple Streams, any number of streamish window systems, or anything else that might involve gf discrimination on stream type.
Synonym streams present two practical problems. First, the operational class of the stream is not apparent via the actual class of the stream. Second, any function that is part of some higher layer implemented on streams (e.g. draw-rectangle) must be extended to work with a synonym-stream. Plausible safety nets to do this automatically such as no-applicable-method typically have unacceptable overhead or indirectness.
I forget whether typical CLIM operators would work on synonym streams, or whether anyone ever considered the issue...
is this objection specific to synonym streams? is not make-synonym-stream typical of the standard cl stream operations in that it is reluctant to support specialization? in which sense synonym-stream is not in itself an obstacle.
"Steven M. Haflich" wrote:
> Paolo Amoroso wrote: > > Steven M. Haflich writes:
> >>Synonym streams should be deprecated[. ]their existence stands in > >>the way of other, more-useful and more-extensible stream paradigms > >> ...
> > Which paradigms?
> Gray Streams, Simple Streams, any number of streamish window systems, > or anything else that might involve gf discrimination on stream type.
are there arguments against specializing the synonym stream for specific delegation tasks?
are there arguments against relying on special variables, as opposed to slot values, for delegation attributes? wrt the encapsulation ==> robust and predictable issue, there's not much difference between an uninterned symbol and a slot value.
> Synonym streams present two practical problems. First, the operational > class of the stream is not apparent via the actual class of the stream.
iff one uses exactly the synonym-stream class.
> Second, any function that is part of some higher layer implemented on > streams (e.g. draw-rectangle) must be extended to work with a > synonym-stream. Plausible safety nets to do this automatically such as > no-applicable-method typically have unacceptable overhead or > indirectness.
under the same assumptions as above.
> I forget whether typical CLIM operators would work on synonym streams, > or whether anyone ever considered the issue...
"Steven M. Haflich" <smh_no_spiced_...@alum.mit.edu> writes:
> Paolo Amoroso wrote: > > Steven M. Haflich writes:
> >>Synonym streams should be deprecated, not because they might not be > >>useful in overly-clever code, but because their existence stands in > >>the way of other, more-useful and more-extensible stream paradigms > >>that are more compatible with the proven coding abstractions in the > >>language. > > Which paradigms?
> Gray Streams, Simple Streams, any number of streamish window systems, > or anything else that might involve gf discrimination on stream type.
These aren't really paradigms. These are implementation strategies. Synonym streams could be implemented using either of these, though it would be opaque to the user. Moreover, the use of these does not impose any discipline that would cause really anything at all to be dynamically nor statically inferrable from having used one of these so-called paradigms other than "a Turing machine is invoked here".
> Synonym streams present two practical problems. First, the operational > class of the stream is not apparent via the actual class of the stream.
I'm not sure I get your point here.
> Second, any function that is part of some higher layer implemented on > streams (e.g. draw-rectangle) must be extended to work with a > synonym-stream.
"might have to be extended". It seems to me that synonym streams _could_ be a subclass of a gray stream or a simple stream.
> Plausible safety nets to do this automatically such as > no-applicable-method typically have unacceptable overhead or > indirectness.
> I forget whether typical CLIM operators would work on synonym streams, > or whether anyone ever considered the issue...
I'm sure it was originally considered when synonym streams were invented since the Lisp Machine uses both synonym streams and "new flavors" (essentially a syntactic variant of CLOS) pretty extensively in the implementation of Dynamic Windows (conceptually very similar to CLIM).
CL, as usual, didn't pick up the whole of what the LispM had to offer, and so suffers from its desire to "be conservative".
Kent M Pitman wrote: >>Gray Streams, Simple Streams, any number of streamish window systems, >>or anything else that might involve gf discrimination on stream type.
> These aren't really paradigms. These are implementation strategies.
Sorry for being away from this thread so long, but I really cannot agree with the above statement. All these things are not implementation strategies; they are APIs avaialble to client code, even extensible APIs.
If one's focus considering streams is the implementation of the parochial ANS stream capabilities, everything is merely an implementation strategy and the details don't matter at all to the user programmer. But the ANS avoids most of the obvious real-world real-computing-universe requirements on streams. In the age of MACLISP, everything interesting passed through a tty. Today, nothing interesting interesting passes through a tty.
>>Synonym streams present two practical problems. First, the operational >>class of the stream is not apparent via the actual class of the stream.
> I'm not sure I get your point here.
The point is that the implementor of any module that defines non-CL-package operators must arrange for these operators to discriminate and delegate for synonym etc. streams. or else document that they won't work. The former is a tedious and costly load on otherwise sane API implementations.
> "might have to be extended". It seems to me that synonym streams _could_ > be a subclass of a gray stream or a simple stream.
But a synonym strteam would not automatically be a subclass of a terminal-stream-with-double-spacing or a clim:presenting-stream unless the implementors of these protocols make it so.
> I'm sure it was originally considered when synonym streams were invented > since the Lisp Machine uses both synonym streams and "new flavors" > (essentially a syntactic variant of CLOS) pretty extensively in the > implementation of Dynamic Windows (conceptually very similar to CLIM).
There was a time, before they went bankrupt, that Symbolics had lotsa money and lotsa programmers, and could devise that synonym streams would delegate transparently for all the defined operators. But if I'm a little developer trying to write a window system, say, that back ends on the XUL rendering language of Mozilla, I might be annoyed that I have to write methods for every API function I create for each kind of delegating ANSI stream, or else document that they won't work.
I repeat my opinion that the ANS delegating streams (synonym, contatenating, broadcast) are a failed experiment incompatible with modern oo programming and which remain an unfortunate weight upon the language. I think they shold be deprecated.
> but I really cannot agree > with the above statement. All these things are not implementation > strategies; they are APIs avaialble to client code, even extensible APIs.
> ... > >>Synonym streams present two practical problems. First, the operational > >>class of the stream is not apparent via the actual class of the stream.
> > I'm not sure I get your point here.
> The point is that the implementor of any module that defines non-CL-package > operators must arrange for these operators to discriminate and delegate for > synonym etc. streams. or else document that they won't work. The former is > a tedious and costly load on otherwise sane API implementations.
> > "might have to be extended". It seems to me that synonym streams _could_ > > be a subclass of a gray stream or a simple stream.
> But a synonym strteam would not automatically be a subclass of a > terminal-stream-with-double-spacing or a clim:presenting-stream unless > the implementors of these protocols make it so.
but no specialization relation is autmatic. if one neglects issues of finalization and encapsulation which would preclude a subclass, why can not a synonym stream not treat these as protocol classes to be specialized, or, alternatively, assuming that the api is intended to be extended, why can't it specialize some abstract class, for which the abstract functions in the interface are then written? yes, the implementors of the protocol cannot make it so, but they can allow the users to.
as i noted in my earlier query, delegation in lisp depends on a binding for the referent. synonym streams effect this with a special variable. object-based delegation can support it with a slot binding. it would be possible to do it with closed-over variables, but i'm not acquainted with implementation which use that technique. given this requirement, that is, that the referent is bound somewhere, the only difference i can discern between the special-binding-based delegation and the slot-based delegation is, well, the nature of the binding.
> > I'm sure it was originally considered when synonym streams were invented > > since the Lisp Machine uses both synonym streams and "new flavors" > > (essentially a syntactic variant of CLOS) pretty extensively in the > > implementation of Dynamic Windows (conceptually very similar to CLIM).
> There was a time, before they went bankrupt, that Symbolics had lotsa money > and lotsa programmers, and could devise that synonym streams would delegate > transparently for all the defined operators. But if I'm a little developer > trying to write a window system, say, that back ends on the XUL rendering > language of Mozilla, I might be annoyed that I have to write methods for > every API function I create for each kind of delegating ANSI stream, or else > document that they won't work.
this is where i don't understand why that is a problem. that make-synonym-stream does not include a means to specify the class of the stream is a problem. which problem is common to all elements of the standard stream interface, not particular to synonym streams.
> I repeat my opinion that the ANS delegating streams (synonym, contatenating, > broadcast) are a failed experiment incompatible with modern oo programming > and which remain an unfortunate weight upon the language. I think they > shold be deprecated.
what is the argument against specializing them. if it were possible to specify the concrete class, to the standard creation functions, what problem would remain?
if i take a tiny example. let's say i would like to generate svg. i have these data objects which need to be serialized. in order to support delegation, there has to be some provision for it in the api protocol.
in this case, the base generic function and default definitions are all generated via macros. like
which is not that complex and provides for any class which specializes delegate-context and implements delegate-referent. it is not material whether the class specializes synonym-stream or some other abstract class. assuming that the projection operations above are so defined, the alternative delegation forms do not look very different.
(defClass svg-presenter () () (:documentation "the protocol class which specifies the presence of the svg presentation methods"))
(defClass svg-context (aspect-context) ((stream :initform nil :initarg :stream :reader context-stream :documentation "bound to an output stream for svg-envoded output.")) (:documentation "a context which implements graphics operations as svg serialization."))
(defclass svg-wrapper-context (delegate-context svg-presenter stream) ((context :reader effective-context :initarg :context)) (:documentation "a wrapper which implements svg presentation methods through delegation to a context which implements graphics operations."))
(defParameter *svg-output* nil)
(defClass svg-synonym-context (delegate-context svg-presenter synonym-stream) () (:default-initargs :symbol '*svg-output*) (:documentation "a synonym context which delegates through a spacial variable binding rather than a slot binding."))
(defMethod .og.::line*3 ((context svg-context) x1 y1 z1 x2 y2 z2 &optional aspects) "draw a line given two extreme 3d vertices" (svg-line*3 x1 y1 z1 x2 y2 z2 aspects))
(defMethod .og.::line*2 ((context svg-context) x1 y1 x2 y2 &optional aspects) "draw a line given two extreme 2d vertices" (svg-line*3 x1 y1 0.0d0 x2 y2 0.0d0 aspects))
(defMethod .og.::line ((context svg-context) l1 l2 &optional aspects) "draw a line given the end locations (2-d or 3-d) in object/world coordinates" ;; ignoring that svg is only 2d (with-location-coordinates (((x1 y1 z1) l1) ((x2 y2 l2) l2)) (svg-line*3 x1 y1 z1 x2 y2 l2 aspects)))
the projection interface looks something like this
(with-projection-context (*svgc*) (let ((line (make-instance 'line :location #@(|3| 0.0 0.0 0.0) :end #@(|3| 1.0 1.0 1.0)))) (format *svg-wc* "~%~%<!-- presenting to a svg-wrapper-context: -->~%") (present line *svg-wc*) (format *svg-sc* "~%~%<!-- presenting to a svg-synonym-context: -->~%") (present line *svg-sc*)))
wherein i discern no difference between synonym classes and wrapper classes. do the problems surrounding a terminal-stream-with-double-spacing a clim:presenting-stream has a shape which is so different from this?