FastShowPretty
Original module -- derives FastShowPretty instances for pretty-printing case classes, sealed traits, Scala 3 enums, and more with configurable indentation. Outputs Scala-literal-style text with named fields, nested indentation, and proper escaping.
Installation
sbt
Cross-platform (JVM / Scala.js / Scala Native):
Quick start
Pretty-printing a nested case class
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
case class Address(street: String, city: String)
case class Person(name: String, age: Int, address: Address)
val person = Person("Alice", 30, Address("123 Main St", "New York"))
println(FastShowPretty.render(person, RenderConfig.Default))
// expected output:
// Person(
// name = "Alice",
// age = 30,
// address = Address(
// street = "123 Main St",
// city = "New York"
// )
// )
API
Derivation methods
| Method | Returns | Description |
|---|---|---|
FastShowPretty.render[A](value, config) |
String |
Inline render (no instance allocation) |
FastShowPretty.derived[A] |
FastShowPretty[A] |
Sanely-automatic (given/implicit) |
FastShowPretty.render is the primary entry point -- it derives and renders in a single call with no intermediate instance. The derived method is available for cases where you need to pass an instance around.
Type class interface
FastShowPretty[A] provides one method:
| Member | Signature | Description |
|---|---|---|
render |
(sb: StringBuilder, config: RenderConfig, level: Int)(value: A): StringBuilder |
Render a value into a StringBuilder at the given indentation level |
Configuration
RenderConfig controls indentation. Unlike the builder pattern used by other modules, RenderConfig is a simple case class with predefined constants:
import hearth.kindlings.fastshowpretty._
// Use a predefined config
val output = FastShowPretty.render(myValue, RenderConfig.Default)
// Or construct a custom one
val custom = RenderConfig(indentString = "...", startLevel = 0)
val output2 = FastShowPretty.render(myValue, custom)
Predefined configs
| Config | Indent string | Description |
|---|---|---|
RenderConfig.Default |
2 spaces | Standard pretty-printing |
RenderConfig.Compact |
empty string | No indentation (all fields on separate lines but not indented) |
RenderConfig.Tabs |
tab character | Tab-based indentation |
RenderConfig.FourSpaces |
4 spaces | Wider indentation |
Config fields
| Field | Type | Default | Description |
|---|---|---|---|
indentString |
String |
" " (2 spaces) |
String prepended per indentation level |
startLevel |
Int |
0 |
Initial indentation level |
Usage examples
Different indentation styles
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
case class Point(x: Int, y: Int)
val p = Point(10, 20)
// 2-space indent (default)
println(FastShowPretty.render(p, RenderConfig.Default))
// expected output:
// Point(
// x = 10,
// y = 20
// )
// Tab indent
println(FastShowPretty.render(p, RenderConfig.Tabs))
// expected output:
// Point(
// x = 10,
// y = 20
// )
// 4-space indent
println(FastShowPretty.render(p, RenderConfig.FourSpaces))
// expected output:
// Point(
// x = 10,
// y = 20
// )
// Compact (no indent)
println(FastShowPretty.render(p, RenderConfig.Compact))
// expected output:
// Point(
// x = 10,
// y = 20
// )
Collections and maps
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
case class Team(name: String, members: List[String])
val team = Team("Engineering", List("Alice", "Bob", "Charlie"))
println(FastShowPretty.render(team, RenderConfig.Default))
// expected output:
// Team(
// name = "Engineering",
// members = List(
// "Alice",
// "Bob",
// "Charlie"
// )
// )
Sealed traits
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape
val shape: Shape = Circle(5.0)
println(FastShowPretty.render(shape, RenderConfig.Default))
// expected output:
// (Circle(
// radius = 5.0d
// )): Shape
Recursive data types
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
case class Tree(value: Int, children: List[Tree])
val tree = Tree(1, List(
Tree(2, Nil),
Tree(3, List(Tree(4, Nil)))
))
println(FastShowPretty.render(tree, RenderConfig.Default))
// expected output:
// Tree(
// value = 1,
// children = List(
// Tree(
// value = 2,
// children = List()
// ),
// Tree(
// value = 3,
// children = List(
// Tree(
// value = 4,
// children = List()
// )
// )
// )
// )
// )
Using the derived instance directly
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
case class Person(name: String, age: Int)
// Sanely-automatic: resolved by the compiler via FastShowPretty.derived
val instance = implicitly[FastShowPretty[Person]]
val sb = instance.render(new StringBuilder, RenderConfig.Default, 0)(Person("Alice", 30))
println(sb.toString)
// expected output:
// Person(
// name = "Alice",
// age = 30
// )
Annotations
| Annotation | Target | Description |
|---|---|---|
@sensitiveData |
Field or type | Replaces the rendered value with [redacted] |
@sensitiveData("reason") |
Field or type | Replaces the rendered value with [redacted: reason] |
Import annotations from hearth.kindlings.fastshowpretty.annotations.
Redacting sensitive fields
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
import hearth.kindlings.fastshowpretty.annotations.sensitiveData
case class User(name: String, @sensitiveData password: String)
println(FastShowPretty.render(User("Alice", "s3cret"), RenderConfig.Default))
// expected output:
// User(
// name = "Alice",
// password = [redacted]
// )
Redacting with a reason
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
import hearth.kindlings.fastshowpretty.annotations.sensitiveData
case class User(name: String, @sensitiveData("PII") email: String, age: Int)
println(FastShowPretty.render(User("Alice", "alice@example.com", 30), RenderConfig.Default))
// expected output:
// User(
// name = "Alice",
// email = [redacted: PII],
// age = 30
// )
Redacting an entire type
//> using scala 2.13.18
//> using dep com.kubuszok::kindlings-fast-show-pretty:0.2.0
import hearth.kindlings.fastshowpretty._
import hearth.kindlings.fastshowpretty.annotations.sensitiveData
@sensitiveData("financial data") case class CreditCard(number: String, cvv: String)
case class Checkout(item: String, card: CreditCard)
println(FastShowPretty.render(CreditCard("4111-1111-1111-1111", "123"), RenderConfig.Default))
// expected output:
// [redacted: financial data]
println(FastShowPretty.render(Checkout("Widget", CreditCard("4111", "123")), RenderConfig.Default))
// expected output:
// Checkout(
// item = "Widget",
// card = [redacted: financial data]
// )
Primitive rendering
FastShowPretty renders primitives with type-disambiguating suffixes, matching Scala literal syntax:
| Type | Example value | Rendered as |
|---|---|---|
Boolean |
true |
true |
Byte |
42 |
42.toByte |
Short |
42 |
42.toShort |
Int |
42 |
42 |
Long |
42L |
42L |
Float |
3.14f |
3.14f |
Double |
3.14 |
3.14d |
Char |
'a' |
'a' |
String |
"hello" |
"hello" |
Strings are properly escaped (quotes, newlines, etc.).
Comparison with kittens Show
FastShowPretty serves a similar purpose to cats.Show derived via kittens, but produces indented, multi-line output suitable for debugging complex nested structures. It also renders primitives with type-disambiguating suffixes (e.g., 42L for Long, 3.14f for Float).
For runtime performance comparison with kittens' Show, see the Cats derivation benchmarks. FastShowPretty uses the same macro-generated approach, so performance characteristics are similar to Kindlings' Show derivation.
Debugging
Import the debug package to log the generated code during compilation:
Or enable project-wide via scalac option: