1. 14
    1. 4

      Happy to answer arbitrary questions about pretty printing, as I now know way too much about the subject.

      Recommendations on which pretty printing approach to use, depending on your use case.

      Do you need dynamic alignment, where a line gets indented to align with stuff on the line above it like the example below? Or can you get away with just fixed alignment where e.g. all indentation is a multiple of 4 spaces? If you only used fixed alignment and not dynamic alignment, things get much faster and an order of magnitude simpler, and you can use Wadler’s algorithm (used in prettier.js, Haskell, Rust, Gleam, Elixir), or my slight variation on it described in this post.

      // dynamic alignment:
      let foo = f(argument1,
                  argument2);
      

      If you really need dynamic alignment, you have a couple choices. One is the approach in a paper I co-authored recently, now (or soon to be?) used in Racket: https://sorawee.github.io/pretty-expressive-oopsla23-artifact/full-paper.pdf Another is the graph-search approach that clang-format and Dart and Scala use: https://geirsson.com/assets/olafur.geirsson-scalafmt-thesis.pdf

      I’m very embarrassed that we missed this thesis in our literature search. It is, to my understanding, the first formal write-up of the approach used by clang-format etc. (Clang-format itself is written in an ad-hoc way that mixes the pretty printing logic in with the languages it supports, making it hard to talk about what ‘its algorithm’ is. But it looks like the Scala printer rectifies this.) Sorawee and I are going to read and discuss this and compare it to our printer.

      For a more detailed overview of the pretty printer space, that Scala printer thesis has what looks like an excellent survey: https://geirsson.com/assets/olafur.geirsson-scalafmt-thesis.pdf

      And our paper has an expressiveness and time-complexity analysis of many printers: https://sorawee.github.io/pretty-expressive-oopsla23-artifact/full-paper.pdf

      1. 2

        A minor point: over a r/ProgrammingLanguages I pointed out that the generalized “alternative” construct has been available (if I understand correctly) in François Pottier’s PPrint library for a while.

        1. 3

          It’s also in Wadler’s printer! It’s just not exposed. The new thing in this post is the flat construct (not to be confused with Wadler’s flatten).

          (Answered in more detail on Reddit.)

        2. 2

          Update: took a closer look at the Scala printer, and there’s not as much going for it as I’d hoped. Implementation is language specific and ad-hoc, so it lacks formal guarantees. It’s searching exponentially large graphs, and if it isn’t able to find an optimal path quickly it just kinda picks something.

        3. 2

          It’d be fun to use a dependently-typed language to implement this with enforcement of The Rule.