PPrint for Kotlin

This is a port of Li Haoyi’s excellent Scala pretty-printing library into Kotlin print. (As well as Li Haoyi’s excellent Ansi-Formatting library Fansi!)

Usage

Add the following to your build.gradle.kts:

implementation("io.exoquery:pprint-kotlin:1.0.0")

Then use the library like this:

import io.exoquery.pprint

data class Name(val first: String, val last: String)
data class Person(val name: Name, val age: Int)
val p = Person(Name("Joe", "Bloggs"), 42)
println(pprint(p))

It will print the following beautiful output:

PPrint-Kotlin supports most of the same features and options as the Scala version. I will document them here over time however for now please refer to the Scala documentation

Nested Data and Complex Collections

PPrint excels at printing nested data structures and complex collections. For example:

Lists embedded in objects:

data class Address(val street: String, val zip: Int)
data class Customer(val name: Name, val addresses: List<Address>)

val p = Customer(Name("Joe", "Bloggs"), listOf(Address("foo", 123), Address("bar", 456), Address("baz", 789)))
println(pprint(p))

Maps embedded in objects:

data class Alias(val value: String)
data class ComplexCustomer(val name: Name, val addressAliases: Map<Alias, Address>)

val p =
  ComplexCustomer(
    Name("Joe", "Bloggs"),
    mapOf(Alias("Primary") to Address("foo", 123), Alias("Secondary") to Address("bar", 456), Alias("Tertiary") to Address("baz", 789))
  )
println(pprint(p))

Lists embedded in maps embedded in objects:

val p =
  VeryComplexCustomer(
    Name("Joe", "Bloggs"),
    mapOf(
      Alias("Primary") to
        listOf(Address("foo", 123), Address("foo1", 123), Address("foo2", 123)),
      Alias("Secondary") to
        listOf(Address("bar", 456), Address("bar1", 456), Address("bar2", 456)),
      Alias("Tertiary") to
        listOf(Address("baz", 789), Address("baz1", 789), Address("baz2", 789))
    )
  )
println(pprint(p))

Infinite Sequences

Another very impressive ability of PPrint is that it can print infinite sequences, even if they are embedded other objects for example:

data class SequenceHolder(val seq: Sequence<String>)

var i = 0
val p = SequenceHolder(generateSequence { "foo-${i++}" })
println(pprint(p, defaultHeight = 10))

PPrint is able to print this infinite sequence without stack-overflowing or running out of memory because it is highly lazy. It only evaluates the sequence as it is printing it, and the printing is always constrained by the height and width of the output. You can control these with the defaultHeight and defaultWidth parameters to the pprint function.

Black & White Printing

The output of the pprint function is not actually a java.lang.String, but a fansi.Str. This means you can control how it is printed. For example, to print it in black and white simple do:

import io.exoquery.pprint.PPrinter

val p = Person(Name("Joe", "Bloggs"), 42)

// Use Black & White Printing
println(pprint(p).plainText)

Removing Field Names

By default pprint will print the field names of data classes. You can remove these by using showFieldNames = false:

val p = Person(Name("Joe", "Bloggs"), 42)
println(pprint(p, showFieldNames = false))

For larger ADTs this dramatically reduces the amount of output and often improves the readability.

Extending PPrint

TODO

Fansi

TODO

GitHub

View Github