uip | title | description | author | status | type | category | created |
---|---|---|---|---|---|---|---|
0119 |
Pretty Printer Improvements |
Make the Hoon pretty printer robust and customizable |
~sidnym-ladrut (@sidnym-ladrut) |
Draft |
Standards Track |
Hoon |
2024-01-20 |
The Hoon pretty printer (i.e. standard library 5c) is broadly
responsible for transforming $type
and $vase
nouns into $tank
equivalents, generally for the purpose of rendering these nouns in a
human-legible format. While its current implementation admirably performs this
task for nearly all simple and semi-complex nouns, it suffers from performance
and legibility problems for common nontrivial inputs such as meta-$vase
nouns, %lull
types, and noun comparisons (e.g. via +dext:nest:ut
).
Additionally, the pretty printer's limited set of customization options forces
all output to exist at one of two levels of abstraction: extremely verbose (the
default) or extremely terse (for types annotated with $+
). This proposal
contains an array of suggestions to address these deficiencies in an iterative
and user-oriented manner.
There are numerous simple pretty printer invocations that render a ship unusable or produce largely illegible results. The following is a short list of common representative examples:
!>(*)
,+<..
: Processes for several minutes before ultimately crashing the ship with arecover: dig: meme
error.->:!>(*noun)
: Fails to print any output, exiting with adojo: failed to process input
error.^-((unit dome:clay) *dome:clay)
: Produces nearly 900 lines of output for a difference spanning a single line.
The lack of per-type printing customization also makes it difficult to tune the pretty printer's level of detail to the user's operative level of abstraction. This presents problems most commonly for developers at both ends of the abstraction range (i.e. core and app developers), who encounter troubles with outputs for nouns like:
-:!>(*peer-state:ames)
: Prints#t/#peer-state
, which is unhelpful for a core developer attempting to debug/inspect this structure at a nontrivial level of detail.^-((tree [@t @]) (malt ~[[';' 1] [':' 2] ['!' 3]]))
: Prints the tree using set-like{…}
syntax, which makes it difficult for a core developer that wants to see the node ordering of the underlying tree structure.mint:ut
: Prints the+mint
arm using the pretty printer's<X.abc …>
summary syntax, which is difficult to parse for an app developer trying to determine the function's signature (doccords helps with this somewhat, but cannot easily print door arms).
Joe (
~master-morzod
) had some interesting ideas for how to rewrite the pretty printer in a way that might be easier—and maybe even more principled—involving using the%hint
type for types, treating that like a mark, and then allowing you to write a function that describes how data of that mark could be pretty printed. Then, you could write a pretty printer where the logic of the printer itself doesn't know anything about those (marks) and is a very dumb genric—and that could be really nice.– Ted Blackman (
~rovnys-ricfer
), "Developer Week: Core Dev AMA (2022)"
- In a similar vein to
+easy-print
, develop a parallel version of the+us
door that leverages the existing[%hint [%know mark=@tas] …]
type structure to enable%mark
-like type identification and corresponding custom printing functions. This new door will assume the temporary name+ur
.- Introduce the concept of a pretty printer gate type
$ppin
with signature$-([inp=(each type vase) bas=$-((each type vase) tank)] tank)
.- The
bas
argument is the base pretty printer gate that will be used by+ur
, which is passed to each$ppin
in order to enable recursive$tank
building. - The following is a rough sketch of a
$ppin
gate for the$unit
type:|= [inp=(each type vase) bas=$-((each type vase) tank)] ^- tank ?- -.inp %& :: type ?> ?=(%hint -.p.inp) :+ %rose [" " "u(" ")"] [(bas %& q.p.inp)]~ :: %| :: vase ?> ?=(%hold -.p.p.inp) =+ ;;(=type (slot 2 (~(play ut p.p.p.inp) q.p.p.inp))) :+ %rose [" " "[" "]"] :~ [%leaf '~' ~] (bas %| type q.p.p.inp) == ==
- The
- Make the
+ur
door sample a copy of+us
with the additional internal state of a verbosity settingveb
and a pretty printer mappin
:++ ur => |% +$ ppin $-([(each type vase) $-((each type vase) tank)] tank) -- =+ :* veb=*?(%base %most %lest) :: default verbosity pin=*(map term ppin) :: print overrides == =+ sur=type :: … arms start here …
veb
is the verbosity setting used by the pretty printer for all type marks not spanned bypin
, which has three proposed flavors: (1)%base
(the current behavior), (2)%most
(raw noun dumps), and%lest
(aggressive%know
name substitution)pin
enumerates a set of type marks and associated printing functions that will override the defaultveb
printing behavior for those types.
- Extract the heuristic type identification logic from
+dole:us
into a new gate+doxx:ur
with signature$-(type type)
that annotates the input with%know
notes for the common types (e.g.list
,tree
,unit
, etc.).
- Introduce the concept of a pretty printer gate type
- Analogous to the above, create a parallel door to
+ut
nominally named+uq
that contains a modified nesting algorithm in+dext:nest:uq
that reports$type
errors as diffs instead of a sequence of comparative dumps. This could be approached a couple of different ways:- Text Diff: Leverage the text diffing
algorithm
currently in
zuse
to present a line-based diff. - Tree Diff: Leverage the noun diffing
algorithm implemented by
~racfer-hattes
(i.e. @ilyakooo0) in order to present a tree-based diff.
- Text Diff: Leverage the text diffing
algorithm
currently in
- Segregate these changes to a standalone
/lib/uip119/hoon
file, which can be integrated into/sys/hoon/hoon
as part of a futurehoon
Kelvin decrement.+ur
and+uq
are designed to be drop-in replacements for+us
and+ut
, respectively.- Optionally, introduce
$+
hints to common types recognized by+doxx:ur
in/sys/hoon/hoon
. - Optionally, consider making the internal
+us
configuration more accessible (like the+rs
door) at the cost of backward compatibility (breaking change to all external+us
door invocations).
- Using an embedded and customizable pretty printer facility in
+us
has two major advantages:- The existing "special case" print functions (e.g. for
$tree
s,$face
s, etc.) can be integrated into a unified and holistic architecture. - Each
$type
-based print function can be fine tuned to the abstraction layer of the developer—core developers can use verbose, near-noun representations and app developers can use terse, near-$+
/%know
presentations.
- The existing "special case" print functions (e.g. for
- Presenting type nesting failures as diffs reduces output noise, drastically reducing deciphering time and improving developer experience.
- Development can safely be performed using parallel arms and structures, enabling extensive vetting/testing prior to integration.
- Using the new stack of
$type
-related structures will break backward compatibility and require andhoon
Kelvin decrement. - Replacing
+us
and+ut
with their the parallel counterparts+ur
and+uq
will break backward compatibility with all code dependent on pretty printer outputs, but will retain interface continuity. Optionally, the+us
door can be replaced to make pretty printer overriding simpler at the cost of an interface discontinuity.
~master-morzod
(i.e. @joemfb) for providing architectural sketches.~rovnys-ricfer
(i.e. @belisarius222) for motivating this project.~master-morzod
(i.e. @joemfb),~rovnys-ricfer
(i.e. @belisarius222), and~datnut-pollen
(i.e. @drbeefsupreme) for providing a wealth of prior art and examples in%/lib/xray/hoon
,%/lib/language-server/easy-print/hoon
, and%/lib/dprint/hoon
(respectively).
Copyright and related rights waived via CC0.