[e-lang] TextWriter/__printOn expectations/contract questions
Kevin Reid
kpreid at attglobal.net
Fri Mar 4 23:39:42 EST 2005
On Mar 4, 2005, at 19:25, Mark Miller wrote:
> Kevin Reid wrote:
>> But what qualifies as printing? What if, for example, 'tw' produced
>> output like this, creating element separators and showing alleged
>> FQNs?
>> __main$readOnlyMap<org.erights.e.elib.tables.FlexMap<"[",
>> org.erights.e.meta.java.math.EInt<"1">, " => ",
>> org.erights.e.meta.java.math.EInt<"2">, "]">, ".readOnly()">
>
> I think showing the element separation should not be considered a
> violation. I think showing the FQNs, perhaps, should. But I don't feel
> strongly about the latter.
My current idea is that this is exactly the case: element separation is
information *explicitly* given - though currently discarded - to the
TextWriter by invocations of print/quote/write, but FQNs are attributes
of the objects being printed that would *not* be mediated by the
object's __printOn.
Also, allowing visible element separation is probably not avoidable if
we were to implement my item #3, since there would therefore be
user-written replacements for the 'append characters to stream'
operation.
>> Or, less excessively, what if 'tw' arranged to print cycles more
>> informatively, as Common Lisp's printer does? This would reveal some
>> information about sameness.
>> ? def thingy := [def a := [[].diverge(), a], def b :=
>> [[].diverge(), b]]
>> # value: [[[].diverge(), ***CYCLE***], [[].diverge(), ***CYCLE***]]
>> ? toQuoteWithCycleTagging(thingy)
>> # value: "[def cycle_1 := [[].diverge(), cycle_1], def cycle_2 :=
>> [[].diverge(), cycle_2]]"
>
> I think this would not only be fine, but would be a welcome
> improvement on the current system.
It may be expensive to implement, though. Three approaches:
* keeping all the 'output so far' for each object being printed in
separate buffers, or in a not-just-append-only mutable buffer with
indexes for each object, until its __printOn returns, so that a 'def'
can be inserted if needed before the object's print is appended to the
main output stream.
* for each object, call its __printOn twice; once to determine if there
is a cycle (with a 'dummy' TextWriter that produces no output), and
again to actually print, having output the 'def' if necessary.
* find out what Common Lisp implementations do, and copy that.
>> 2. Inward privacy
>> What can an object participating in printing expect to not be
>> revealed *to* its components?
>> For example, suppose there was code in multiple (programming)
>> languages operating in the same Vat, and using the same ELib
>> primitive objects. TextWriter could be extended to provide access to
>> an object describing the syntax of the language. ...
>
> If I understand correctly, this is not revealing information about the
> container to the contained that the container does not choose to
> reveal. Rather, it is allowing information in the TextWriter to be
> passed through to the contained.
Yes.
> There is a danger here. But so long as the extra information is
> limited to data and DeepPassByCopy objects, it would seem we already
> have this danger with the TextWriter indent level. We should examine
> how one might abuse such a channel.
As far as I know, TextWriter is currently a write-only interface - the
indent string may be appended to, and employed in output, but not read
by the client of the TextWriter. What I am proposing is therefore a
completely new data path.
One useful feature I can think of: offer a method of the indent/withFoo
sort which returns a sub-writer with all readable attributes discarded
or reset-to-defaults. Thus a container can prevent any inward
information flow.
An extra-strict form would do so by default, only passing the 'extra
information' if the container explicitly allows it. This is how
TextWriters in E-on-CL currently handle the print-or-quote attribute.
However, I think this might be excessively awkward to use.
> These issues remind be of the Throwable issue: Prior to the
> DarpaBrowser review, I thought that the restriction that one could
> only throw DeepPassByCopy data was adequate protection. Perhaps
> there's the same rude surprise lurking here?
It seems unlikely. This is (relatively) explicit data flow, whereas the
problem with exceptions is/was (assuming the DeepPassByCopy
restriction) that they provide a path for data flow which is implicit
in the code, and therefore 'outside' of the capability rules we expect.
I have some further thoughts on exception semantics and the solution I
proposed, which I will post later, as they're as yet incomplete and
don't really fit in with this message.
>> Would allowing such parameterization break any intended properties of
>> the __printOn/TextWriter interface?
>> (The current interface, of course, already reveals some information:
>> the time-ordering of __printOn messages vs. other messages.)
>
> The question is hard to answer, since it depends on programmer
> psychology. The main "intended property" is that the programmer can
> write useful __printOn methods using the standard pattern and the
> TextWriter guard, and be fairly confident, without needing to think
> about it hard, that they haven't thereby blown the normal object-cap
> security properties that the rest of their program may be depending
> on.
Indeed. Perhaps I should have written my original message's questions
in the form "Would this behavior be unpleasantly surprising to you?"
and asked anyone who knows E to respond.
>> 3. Extension of output types
>> What if the kinds of 'primitive' output (currently String, or rather,
>> stream-of-Characters) were extended? For example, styled text (or
>> markup), or 'printing' an image?
>> At some point, it becomes more appropriate to provide a custom
>> visitor interface, but __printOn has the advantage of being
>> universal, so such special 'printing' could pass through preexisting
>> collection/container/proxy/slot/... objects.
>
> I don't see a problem with that, but I haven't really thought about it.
I forgot to mention something in my original message:
I had been assuming (I forget why) that it is expected that invoking a
TextWriter will never have arbitrary synchronous side effects (that is,
they will at most be appending to a buffer which can be read but takes
no further synchronous action itself).
Is this a useful constraint?
* Requiring this means that the 'extended type' TextWriters I suggest
above must have confined 'append' implementations, and regular
TextWriters cannot write to user-defined streams.
* Not requiring this means that objects must be aware that calling
tw.print() in their __printOn may cause calls to themselves which could
alter the state that they are in the middle of printing.
Since the latter is not unique to __printOn/1 (consider implementing a
mutable collection's iterate/1), perhaps there should be no particular
protections for __printOn so that the programmer develops the habit of
considering possible side effects from 'callbacks' of any sort.
Also, while I'm talking about everything else about TextWriter: the
name seems rather awkward. "TextWriter" doesn't tell the reader
anything about what it's used for; "Writer" is a Java concept which I
don't think should be involved in a rather significant part of the ELib
model, and "Text" doesn't seem meaningful in this context.
What would you think of renaming the universal-scope guard, and
possibly the class FQN to, say, "Printer"? Since the word "print" is
already used in the method name, this might even make it easier to
learn by having less-unrelated names for pieces of the same system.
(Such a change could be entirely backwards-compatible, as the old noun
could remain.)
--
Kevin Reid <http://homepage.mac.com/kpreid/>
More information about the e-lang
mailing list