Formal Comment
Submitter's name: Marc Feeley Submitter's email: feeley at iro.umontreal.ca Relevant draft: r7rs draft 6
Type: defect Priority: major Relevant section of draft: 6.13.3. Output
Summary: Write procedure is not backwards compatible
R7RS introduces a new output procedure called write-simple, which has the same semantics as the R5RS write procedure. On the other hand, the R7RS write procedure handles shared structures differently than the R5RS. For example :
(let ((x (list 1 2))) (write (list x x)))
displays ((1 2) (1 2)) in an R5RS system and displays (#0=(1 2) #0#) in an R7RS system
To preserve backwards compatibility, it is the version of the write procedure which uses datum labels which should have a different name. In fact SRFI-38 has specified the name write-with-shared-structure for this output procedure. This name should be maintained since it has been implemented with that name in some Scheme systems.
After further discussion with Marc and evaluating Scheme implementations, I think that we have a valid problem here. On the one hand, we have the desire to be able to preserve datum equivalence across a read and a write, on the other the desire to make certain writes produce valid code. Finally, we have the third desire not to make write unstable.
I propose that we can actually do this all with a single procedure write and a parameter print-graph. When print-graph is #f then write has the traditional semantics, where the behavior on cycles is undefined -- particularly, Schemes may print cycles, or they may infinite loop, but in the non-cycle case, they print the simple structure, and do not preserve shared structure. When print-graph is #t then write must print out the datum preserving shared structure.
The above allows write to be backwards compatible, but still enables implementations to implement it safely if they wish to, to handle cycles, but still gives us a way to print shared structure.
I think it is important to provide a safe, default, backwards compatible version of write. In that vein, I would want to require implementations that support shared structure to enable print-graph implicitly when a cycle is detected.
Another option is to have write/shared on the one hand, but then make write use write/shared whenever a cycle is detected if the implementation provides it. However, this creates some problems for folks who might want a write that would infinite loop. The question is whether this is worth it at all?
Using the parameter is more flexible, as it allows us to add an additional switch, where we could say that if print-graph is 'cycle then it will detect cycles, but if it is #f it will not, and could infinite loop.
I think the question comes down to whether we want to require the handling of cycles or not.
WG1 voted to adopt write (labels for cycles only), write-simple (no labels), and write-shared (full labels).