erl_pp(3)

πŸ“¦
version 29.0.6 Β· 3 versions
, in ubuntu stonking

πŸ’– NAME πŸ’–

erl_pp β€” literally just the Erlang pretty printer, no cap πŸ‘‘


✨ DESCRIPTION ✨

okay so bestie, the functions in this module are the whole reason your abstract forms can look aesthetically attractive and actually be suitable for printing. They take abstract forms and give you back glow-ups. πŸ¦‹

Every single function in here returns a (possibly deep) list of characters, and every single one of them generates an error if the form is wrong. No silent fails, she does not play like that πŸ’…

Every function can also take an optional argument, which specifies a hook that gets called if something tries to print an unknown form.

⚠️ real talk: if you're using these functions to convert abstract code back to Erlang source code, you should first run the enclosing function through legalize_vars/1 so the output is actually semantically equivalent to the abstract code. Do not skip this step, bestie, I am begging πŸ™ˆ

🌸 Known Limitations

It is not possible to have hook functions for unknown forms anywhere other than in expressions. That's it, that's the limitation, periodt.

πŸ‘€ See Also

erl_eval, erl_parse, io


🧚 DATA TYPES 🧚

hook_function()

hook_function() ::
          none |
          fun((Expr :: erl_parse:abstract_expr(),
               CurrentIndentation :: integer(),
               CurrentPrecedence :: non_neg_integer(),
               Options :: options()) ->
                  io_lib:chars()).

The optional argument HookFunction (you'll see it showing up in all the functions below) defines a function that gets called whenever an unknown form shows up somewhere there needs to be a valid expression. If HookFunction is equal to none, then congrats, there is no hook function. πŸŽ‰

The hook function you call is supposed to return a (possibly deep) list of characters. And psst β€” the function expr/4 is super useful inside a hook, iykyk.

If CurrentIndentation is negative, there are no line breaks and only a space is used as a separator. Little detail, big energy. ✨

option()

option() ::
          {hook, hook_function()} |
          {encoding, latin1 | unicode | utf8} |
          {quote_singleton_atom_types, boolean()} |
          {linewidth, pos_integer()} |
          {indent, pos_integer()}.
  • The quote_singleton_atom_types option is used to add quotes to all singleton atom types. yes, all of them. πŸ’…
  • The linewidth option controls the maximum line width for formatted lines (defaults to 72 characters).
  • The indent option controls the indention for formatted lines (defaults to 4 spaces).

options()

options() :: hook_function() | [option()].

πŸ”₯ FUNCTIONS πŸ”₯

  • attribute(Attribute, Options) -> io_lib:chars()
     when
         Attribute :: erl_parse:abstract_form(),
         Options :: options().

Same as form/1,2, but only for the attribute Attribute. It's giving specialized, we love a focused queen πŸ‘‘


  • expr(Expression, Indent, Precedence, Options) -> io_lib:chars()
     when
         Expression :: erl_parse:abstract_expr(),
         Indent :: integer(),
         Precedence :: non_neg_integer(),
         Options :: options().

Prints one expression. Just one. Single. Main character energy only. 🌟

It's useful for implementing hooks (see the section Known Limitations up above, bestie).


  • exprs(Expressions, Indent, Options) -> io_lib:chars()
     when
         Expressions :: [erl_parse:abstract_expr()],
         Indent :: integer(),
         Options :: options().

Same as form/1,2, but only for the sequence of expressions in Expressions. Plural era. πŸ”₯


  • form(Form, Options) -> io_lib:chars()
     when
         Form ::
             erl_parse:abstract_form() | erl_parse:form_info(),
         Options :: options().

Pretty prints a Form, which is an abstract form of a type that's returned by erl_parse:parse_form/1. This is the blueprint, the moment, the whole vibe check. ✨


  • function(Function, Options) -> io_lib:chars()
     when
         Function :: erl_parse:abstract_form(),
         Options :: options().

Same as form/1,2, but only for the function Function. One function, fully served πŸ’…


  • guard(Guard, Options) -> io_lib:chars()
     when
         Guard :: [erl_parse:abstract_expr()],
         Options :: options().

Same as form/1,2, but only for the guard test Guard. Protection spell activated πŸ›‘οΈ


  • legalize_vars(Function) -> erl_parse:abstract_form()
     when Function :: erl_parse:abstract_form().

okay so this one is important, listen up πŸ“’

When the Erlang compiler expands records into tuples, it introduces new variables in the abstract representation. And because that expansion happens on the abstract representation itself, the compiler can safely name those new variables with names that are not syntactically valid in Erlang source code β€” specifically, the name starts with a lowercase letter β€” which guarantees the new names are unique. Clever, lowkey iconic. 🧠✨

But bestie… that strategy causes problems if a user wants to convert the abstract representation back to Erlang source code using the functions in this module. Typically, pattern variables get output as atoms, which changes the semantics of the program. πŸ’€ Not the vibe.

So to solve this, legalize_vars/1 β€” when you run it on the abstract representation of a function β€” returns an equivalent function where all variables will have syntactically valid names. Problem solved, glow restored, we ate. πŸ“


🌈 and that's the whole page! erl_pp really said "I will make your abstract forms pretty and I will not elaborate further," and honestly? respect. πŸ’–

mwah πŸ’‹ this was erl_pp(3), from Ericsson AB, riding with stdlib 8.0.4. stan a pretty printer, xoxo 🫢

πŸ“¦ where this page comes from