A pretty-print, therefore, is not a sequence of side effects
but, at first, a hierarchical structure of print-elements, combinators, and
possibly layout functions. In a second step the, e.g., string
representation of a pretty-print is produced from this structure. Note
that now it is perfectly alright to implement the semantics of the target
structure as member methods of the target structure. Since that structure
is meant for only one purpose, as opposed to the abstract syntax tree
which has many interpretations, there is no drawback involved.
Besides the nice partition between translation- and semantic related code, the target structure also may serve as a logical structure for the final representation. For instance, a mouse click onto a keyword could cause highlighting the whole corresponding statement and its subcomponents. While this could be achieved by back-pointers into the original abstract syntax tree as well, it is cleaner and more appropriate (as no reinterpretation is necessary) to refer to the pretty-print structure. This argument becomes more obvious in case of interpretations whose logical structure bear less resemblance to the abstract syntax tree (e.g., type-checking information).
Also, assuming multiple users are working simultaneously on one abstract syntax tree, multiple intermediate structures allow them to, e.g., use different compilation options to achieve various code results. If semantic results are stored directly in the source structure, this would be a cause for interference.
Furthermore, an intermediate structure is also helpful when aiming for incremental updates.