Fork me on GitHub

PPrint


Example use case of PPrint

PPrint is a module that lets you easily print data structures in a form that is more convenient to use both visually and programmatically. Unlike a traditional .toString, PPrint...

Getting Started


Add the following to your SBT config:

libraryDependencies += "com.lihaoyi" %% "pprint" % "0.4.3"

Or for Scala.js:

libraryDependencies += "com.lihaoyi" %%% "pprint" % "0.4.3"

The above example then showed how to use the default pprint configuration. You can also set up your own custom implicit `pprint.Config` if you want to control e.g. colors, width, or max-height of the output.

The three main methods you need to care about are:

You can of course define your own custom methods if you want to e.g. always log to a log file or some third-party service, or if you want your own custom set of debug information to be printed (instead of what .log provides). See the short implementation of these methods if you want ideas on how to write your own versions.

Pretty-printing is by default defined for most standard library types, as well as case classes and case objects. For other types not supported, it falls back to using toString

scala> pprint.pprintln(new Object())
java.lang.Object@54f880c0

You can configure the pretty-printing by defining your own implicit Config object, to consistently set things like maximum width, height or colors across a codebase. However, if you just want to set these things one-off, you can do that too:

scala> pprint.pprintln(Seq(1, 2, 3))
List(1, 2, 3)

scala> pprint.pprintln(Seq(1, 2, 3), width = 5) // force wrapping
List(
  1,
  2,
  3
)

scala> pprint.pprintln(Seq(1, 2, 3), width = 6, height = 2) // truncate
List(
  1,
...

PPrint also provides the pprint.log function, which automatically adds some context so you can find your printouts later:

scala> class Foo{
     |   def bar(grid: Seq[Seq[Int]]) = {
     |     pprint.log(grid, tag="grid")
     |   }
     | }
defined class Foo

scala> new Foo().bar(Seq(0 until 10, 10 until 20, 20 until 30))
pkg.Foo#bar "grid":12
List(
  Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
  Range(10, 11, 12, 13, 14, 15, 16, 17, 18, 19),
  Range(20, 21, 22, 23, 24, 25, 26, 27, 28, 29)
)

Note how the package name, class name, and method name, along with the optional tag. are all formatted nicely for you to read. This should make it much easier for you to find each individual print later. Just like `pprint.pprintln`, the output is nicely formatted across multiple lines and syntax-highlighted for readability.

Streaming


PPrint is lazy and streaming. Unlike println(x.toString) which marshals the entire string in memory before returning it:

scala> val large = Seq.fill(100000)("ABCDE" * 1000)
scala> println(large.toString)
java.lang.OutOfMemoryError: Java heap space

pprint.pprintln streams the result to standard output, meaning that even for enormous data structures like the one above you can immediately start seeing output:

scala> pprint.pprintln(large)
Seq(
  "ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEA
  BCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABC
  ...

For example, if you have a massive (or even infinite!) data structure and wish to only show a certain amount of it, truncating it is straightforward:

scala> val large = Seq.tabulate(100000)("ABCDE" + _)

scala> pprint.pprintln(large, height=20)
List(
  "ABCDE0",
  "ABCDE1",
  "ABCDE2",
  "ABCDE3",
  "ABCDE4",
  "ABCDE5",
  "ABCDE6",
  "ABCDE7",
  "ABCDE8",
  "ABCDE9",
  "ABCDE10",
  "ABCDE11",
  "ABCDE12",
  "ABCDE13",
  "ABCDE14",
  "ABCDE15",
  "ABCDE16",
  "ABCDE17",
  "ABCDE18",
...

If you want to do something else with the streaming output apart from displaying it on the console, you can also call PPrint.tokenize

scala> val large = Seq.fill(100000)("ABCDE" * 1000)
scala> pprint.PPrint.tokenize(large)
res0: Iterator[String] = non-empty iterator

This gives you an iterator with which you can do whatever you want: stream it to a file, to your logging system, etc.. Even for extremely large data structures, you can use this data structure to page through it and see what it contains without ever materializing the entire string in memory.

Customization


In order to pretty print a type T, you need to have a T: PPrint context bound present. In order to write your own custom pretty printer for some type T, provide an implicit PPrinter[T] in scope.

TPrint


Apart from pretty-printing values, PPrint also allows you to pretty-print types with the pprint.tprint function:

Example use case of TPrint

Apart from calling tprint yourself, you can also add an implicit TPrint[T] param to a function with a type-parameter T and use TPrint[T]#render(cfg: Config) to pretty-print a type. This is handy if you want to write type-printing functionality to an existing function.

Like value pretty-printing, the colors can be configured by the pprint.Config, with import pprint.Config.Colors._ for colored type-printing and import pprint.Config.BlackWhite._ for non-colored type-printing. You can also provide your own implicit TPrint[T] values if you want to customize the type printing of a particular type.

Example use case of Custom TPrint

Version History


0.4.3

0.4.2

0.4.1

0.4.0

0.3.9

0.3.8

0.3.7

0.3.6

0.3.5

0.3.4

0.3.3

0.3.2

0.3.1

0.3.0