[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