Reason #173 • June 22nd, 2026

Print debugging with Kernel#pp

Yes, here's the obvious follow-up to yesterday's Kernel#p post: Kernel#pp, short for "pretty print", courtesy of the pp standard gem.

Works like Kernel#p, but with more readable output for complex objects, especially nested ones:

Ruby
list = 10.times.map do |i|
  { index: i, numbers: 5.times.map(&:itself) }
end

p list
# Output:
# [{index: 0, numbers: [0, 1, 2, 3, 4]}, {index: 1, numbers: [0, 1, 2, 3, 4]}, {index: 2, numbers: [0, 1, 2, 3, 4]}, {index: 3, numbers: [0, 1, 2, 3, 4]}, {index: 4, numbers: [0, 1, 2, 3, 4]}, {index: 5, numbers: [0, 1, 2, 3, 4]}, {index: 6, numbers: [0, 1, 2, 3, 4]}, {index: 7, numbers: [0, 1, 2, 3, 4]}, {index: 8, numbers: [0, 1, 2, 3, 4]}, {index: 9, numbers: [0, 1, 2, 3, 4]}]

pp list
# Output:
# [{index: 0, numbers: [0, 1, 2, 3, 4]},
#  {index: 1, numbers: [0, 1, 2, 3, 4]},
#  {index: 2, numbers: [0, 1, 2, 3, 4]},
#  {index: 3, numbers: [0, 1, 2, 3, 4]},
#  {index: 4, numbers: [0, 1, 2, 3, 4]},
#  {index: 5, numbers: [0, 1, 2, 3, 4]},
#  {index: 6, numbers: [0, 1, 2, 3, 4]},
#  {index: 7, numbers: [0, 1, 2, 3, 4]},
#  {index: 8, numbers: [0, 1, 2, 3, 4]},
#  {index: 9, numbers: [0, 1, 2, 3, 4]}]
    

Essentially, when objects get too wide to be printed on a single line, pp breaks them into multiple lines on natural boundaries like array elements or hash key names.

Return values are handled just like with p, so we can use pp at the end of methods or in assignments without disrupting the logic of our programs.

History

The pp library shipped with Ruby 1.8.0 in 2003, but initially required require "pp" to be made available, and its return value was nil.

Just like with Kernel#p, Ruby 1.9.2, released in 2010, changed return values to return the object(s) printed.

Ruby 2.5, released Christmas 2017, made Kernel#pp available without an explicit require. The initial method is a small lazy-loading trigger: the first pp call requires pp and then delegates to the real implementation.

Ruby 3.1, released Christmas 2021, made pp use the terminal width by default via IO#winsize, so the line breaks better match the actual space available in your shell.