uip | title | description | author | status | type | category | created |
---|---|---|---|---|---|---|---|
0119 |
Pretty Printer Improvements |
Make the Hoon pretty printer robust and customizable |
~sidnym-ladrut (@sidnym-ladrut), ~fidwed-sipwyn |
Final |
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).^-(hoon 1)
: Prints:instead of:-need ?( [p=#1 q=#1] ... rest of the hoon type here ... [%sggr p=?([p=@tas q=#1] @tas) q=#1] [%sgwt p=@ud q=#1 r=#1 s=#1] [%sgcb p=#1 q=#1] [%sgbr p=#1 q=^#1.#hoon] ) -have.@ud
-need.#hoon :: $hoon is named with $+ -have.@ud
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)"
- Rewrite and replace the
+us
door to leverage the existing[%hint [%know mark=@tas] …]
type structure to enable%mark
-like type identification and corresponding custom printing functions.-
The
+us
door new sample will consist of a maximum depthdep
, a verbosity settingveb
and a pretty printer mappin
:++ us => |% +$ tase (each type vase) :: type/vase +$ meta :: recursion metadata $~ [~ ~ 30 %base ~] :: $: saw=(set tase) :: recursive types ids=(map type @) :: types id dep=@ud :: maximum depth veb=?(%base %most %lest) :: default verbosity pin=(map term ppin) :: print overrides == :: +$ base $-([tase meta] (unit [meta tank])) :: base printer +$ ppin $-([tase meta base] (unit [meta tank])) :: custom printer -- :: |% :: … arms start here …
-
dep
will be decremented each time that a%cell
is found, until it reaches zero, at which point printing will stop with[...]
. The maximum depth was primarily introduced to prevent the common issue of the PP hanging for minutes and even causing out of memory errors, observed when trying to print very large outputs (e.g. cores as raw nouns). -
veb
is the verbosity setting used by the pretty printer and has three flavors:
%base %most %lest (map @tas @ud) ?(%~ [n=[p=@t q=@ud] l=[...]]) #t/#map u(1) [~ u=1] u(1) {[p=a q=1] [p=b q=2]} [n=[p='b' q=2] l=~ r=[...]] {[a 1] [b 2]} %base
: Search for a custom printer (ppin), if not found, fallback to the default printer (hardcoded). If the latter is not present, perform name substitution (similar to%lest
).%most
Ignore hints and prints a more extensive output.%lest
For types: Aggressive%know
name substitution for all named types (+$). For vases: Hide all faces. Shorter core printing on bothtype
andvase
printing.pin
enumerates a set of type marks and associated printing functions that will be called for the types named with+$
and for all the cases of$type
.- The pretty printer gate
$ppin
has the following signature:$-([tase meta base] (unit [meta tank]))
.- Where
$tase
is the input to be printed:(each type vase)
. $meta
is used to pass some metadata down into the recursion.- The
base
argument is the base pretty printer gate that will be used by+us
, which is passed to each$ppin
in order to enable recursive$tank
building. - The output is wrapped in a unit because sometimes we return ~ on
error, allowing the
%fork
printer can continue to the next case of the%fork
.
- Where
- The following is a rough sketch of a
$ppin
gate for the$unit
type:|= [inp=tase sen=meta bas=base] ^- (unit [meta tank]) =+ typ=?-(-.inp %& p.inp, %| p.p.inp) ?> ?=([%fork *] typ) =+ yed=(sort ~(tap in p.typ) aor) ?> ?=([* [[%cell * [%face *]] ~]] yed) ?- -.inp %& :: type ?~ res=(bas [%& +<+>+>.yed] sen) ~ `[-.u.res [%rose [" " "u(" ")"] +.u.res ~]] %| :: vase (bas inp sen) ==
- The pretty printer gate
-
-
We introduce $+ hints for commonly used types, such as
$set
,$map
, and$unit
, among others. -
Create a new gate
+doxx
with signature$-(type type)
thats used internaly by the PP to heuristically annotate the input with%know
notes for the%list
types (e.g.tape
,path
,wall
, etc.). -
A new generator
:dojo|pp-config
was introduced, allowing the dev to update%dojo
's PP config. -
The +nest arm has been updated to avoid throwing unnecessary hints (as mentioned with ^-(hoon 1) above). It also now repeats the error location if the output exceeds an arbitrary size of 100 lines (80 characters each).
-
Several default printers have been reworked, including the gate printing, which changed its style from
<number-of-arms.hash ...subject>
to$-(input output)
.
-
- The fixed PP logic is simple and consists of recursively traversing the type/vase, and for each case of $type (%atom, %cell, %core, etc.), calling the associated printer functions, all of which can technically be overridden by a $ppin gate. The dep makes it flexible to constrain outputs on demand, while veb allows for general customization. Together, these changes make the pretty printer robust and customizable.
- Replacing the old pretty printer will break backwards compatibility with any
code that directly calls the pretty printer's door. However, it will not
affect code that uses functions such as
+sell/+skol/+duck:ut/+dunk:ut
. It may also break code that relies on specific pretty printer outputs.
- The
$tank
type could be modified or replaced to be more expressive. - A good noun diff algorithm could be used to make
test.hoon
's diffs smaller.
~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.