PrettyPrinting: optimal layout for code and data
07-29, 17:40–17:50 (UTC), Blue

PrettyPrinting is a library for formatting composite data structures. PrettyPrinting optimizes the layout of the data to make it fit the screen width.

Out of the box, PrettyPrinting can format Julia code and standard Julia containers. It can be easily extended to format custom data types.


If you use Julia REPL to work with JSON or other nested data structures, you may find the way the data is displayed unsatisfactory. If this is the case, consider using PrettyPrinting. Compare:

julia> data = JSON.parsefile("patient-example.json")
Dict{String, Any} with 14 entries:
  "active"               => true
  "managingOrganization" => Dict{String, Any}("reference"=>"Organization/1")
  "address"              => Any[Dict{String, Any}("line"=>Any["534 Erewhon St"], "dis…
  "name"                 => Any[Dict{String, Any}("family"=>"Chalmers", "given"=>Any[…
  "id"                   => "example"
  "birthDate"            => "1974-12-25"
  ⋮
julia> using PrettyPrinting

julia> pprint(data)
Dict(
    "active" => true,
    "managingOrganization" => Dict("reference" => "Organization/1"),
    "address" => [Dict("line" => ["534 Erewhon St"],
                       "district" => "Rainbow",
                       "use" => "home",
                       "postalCode" => "3999",
                       "city" => "PleasantVille",
                       "period" => Dict("start" => "1974-12-25"),
                       "text" => "534 Erewhon St PeasantVille, Rainbow, Vic  3999",
                       "type" => "both",
                       "state" => "Vic")],
    "name" => [Dict("family" => "Chalmers",
                    "given" => ["Peter", "James"],
                    "use" => "official"),
               Dict("given" => ["Jim"], "use" => "usual"),
               Dict("family" => "Windsor",
                    "given" => ["Peter", "James"],
                    "use" => "maiden",
                    "period" => Dict("end" => "2002"))],
    "id" => "example",
    "birthDate" => "1974-12-25",
    ⋮

PrettyPrinting optimizes the layout of the data to make it fit the screen width. It knows how to format tuples, named tuples, vectors, sets, and dictionaries.

PrettyPrinting can also serialize Expr nodes as Julia code. It supports a fair subset of Julia syntax including top-level declarations, statements, and expressions.

The ability of PrettyPrinting to format Expr nodes makes it easy to extend pprint() to user-defined data types. Indeed, it is customary to display a Julia object as a valid Julia expression that constructs the object. This could be done by converting the object to Expr and having pprint() format
it.

For example, let us define a type MyNode modeled after the standard Expr type.

julia> struct MyNode
           head
           args
           MyNode(head, args...) = new(head, args)
        end

The default implementation of show() is not aware of the custom constructor. Moreover, it dumps the whole object in a single line, making it difficult to read.

julia> tree = MyNode("1",
                     MyNode("1.1", MyNode("1.1.1"), MyNode("1.1.2"), MyNode("1.1.3")),
                     MyNode("1.2", MyNode("1.2.1"), MyNode("1.2.2"), MyNode("1.2.3")))
MyNode("1", (MyNode("1.1", (MyNode("1.1.1", ()), MyNode("1.1.2", ()), MyNode("1.1.3", …

We implement function quoteof(::MyNode) to convert MyNode to Expr. We can also override the default implementation of show() to make it use pprint().

julia> PrettyPrinting.quoteof(n::MyNode) =
           :(MyNode($(quoteof(n.head)), $((quoteof(arg) for arg in n.args)...)))

julia> Base.show(io::IO, ::MIME"text/plain", n::MyNode) =
           pprint(io, n)

Now the output is correct Julia code that fits the screen width.

julia> tree
MyNode("1",
       MyNode("1.1", MyNode("1.1.1"), MyNode("1.1.2"), MyNode("1.1.3")),
       MyNode("1.2", MyNode("1.2.1"), MyNode("1.2.2"), MyNode("1.2.3")))

Internally, PrettyPrinting represents all potential layouts of a data structure in the form of a layout expression assembled from atomic layouts, vertical and horizontal composition, and the choice operator. The layout cost function estimates how well the layout fits the screen dimensions. The algorithm for finding the optimal layout is a clever application of dynamic programming, which is described in Phillip Yelland, A New Approach to Optimal Code Formatting, 2016.

The author of PyYAML, LibYAML, HTSQL, and DataKnots.

This speaker also appears in: