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...
If you've enjoyed using PPrint or any of my other libraries, please consider chipping in to support their ongoing development on Patreon! Any amount helps ensure we can continue improving these libraries going forward.
Add the following to your build config:
// SBT
libraryDependencies += "com.lihaoyi" %% "pprint" % "0.9.0"
libraryDependencies += "com.lihaoyi" %%% "pprint" % "0.9.0" // Scala.js/Native
// Mill
ivy"com.lihaoyi::pprint:0.9.0"
ivy"com.lihaoyi::pprint::0.9.0" // Scala.js/Native
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:
pprint.pprintln(value: T): Unit
: pretty-prints the given value to the consolepprint.log(value: T, tag="optional"): Unit
: pretty-prints the given value to the console along with debugging information (class-name, method-name, line-number, an optional tag) to make it easy to find your prints again later. If you're finding yourself putting many debug prints in different files, this makes it much easier to keep them straightpprint.tokenize(value: T): Iterator[String]
: same as pprint.pprintln
, except instead of dumping to standard output it returns an iterator you can consume or pass around to use at your leisure.pprint.apply(value: T): String
: same as pprint.pprintln
, except instead of dumping to standard output it returns a string.
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 = 3) // 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)
| }
| }
defined class Foo
scala> new Foo().bar(Seq(0 until 10, 10 until 20, 20 until 30))
pkg.Foo#bar:13 grid: 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.
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.log(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.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.
PPrint needs to buffer output in some cases in order to correctly wrap/multiline/truncate output. However, in all cases it should need to buffer at most one line
By default, you can use the methods pprint.{log, apply, truncate}
to pretty-print things to stdout, String
s, or streaming Iterator[String]
.
Those methods all take optional arguments, if you want one-off customizations to
how things are printed. pprint.PPrinter.Color
and
pprint.PPrinter.BlackWhite
are provided as default configurations,
where the base pprint
methods are an alias for
pprint.PPrinter.Color
.
If you want to customize the defaults throughout your program, you can do so by
creating your own pretty-printer via pprint.copy
. This lets you change
the default height, width of the pretty-printed output, what colors will be
used for highlighting, as well as passing in additionalHandlers
to
customize pretty-printing of various types.
scala> pprint.pprintln(Math.PI)
3.141592653589793
scala> val pprint2 =
pprint.copy(
additionalHandlers = {
case value: Double => pprint.Tree.Literal(f"$value%1.5f") /* 5 digit precision */
}
)
scala> pprint2.pprintln(Math.PI)
3.14159
Apart from pretty-printing values, PPrint also allows you to pretty-print types
with the pprint.tprint
function:
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.
toString
returning null
#70new java.io.ByteArrayOutputStream()
pprint.log
and pprint.tokenize
still exist, but most of the internals have been totally rewritten.
PPrinter
objects rather than
by passing around implicits. This should be much more intuitive and
harder to get wrong
.toString
, but that sure beats being 10,000x slower!
pprint.stringify
function, thanks to
David Portabella
T
s normal toString
pprint.log
to make it less verbose, added pprint.log2
with the old behavior and verbosity.pprint.log
function, for convenient no-setup console logging and debugging.toString
methods now use those instead of the macro-generated pretty-printer (#115)pprint.tokenize
now takes the default Config
by default, thus removing the need for a special import, just like pprint.pprint
doesakka.http.Http.ServerBinding
Nothing