Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structured markup for throw and trace messages that make Nix more robust #7795

Open
roberth opened this issue Feb 9, 2023 · 0 comments
Open
Labels
error-messages Confusing messages and better diagnostics feature Feature request or proposal language The Nix expression language; parser, interpreter, primops, evaluation, etc

Comments

@roberth
Copy link
Member

roberth commented Feb 9, 2023

Is your feature request related to a problem? Please describe.

Nix diagnostic output has been getting much nicer. Let's continue the trend and make "user space" output nice too.

Error messages that evaluate more than they strictly need to can cause the wrong error to be logged.

Describe the solution you'd like

A data structure that describes the pretty-printable "document". You might call this an embedded markup language, but that sounds more complex than it is.

  • Specify a Nix value representation for pretty-printed items,
  • Interpret this representation to print nicely formatted and color output to the terminal (or anywhere)

Format

  • MVP has the following constructors. These constructors are not primops but rather additions to the Nixpkgs lib. _type = "pretty" marks the interface to Nix's throw, abort and trace.
    • mkColored = c: { _type = "pretty"; color = "red"; content = c; }: render content with specified ANSI color in addition to any other styles
    • mkPrettyValue = v: { _type = "pretty"; value = v; }: pretty-print a Nix value. This is where the robustness claim comes from. Currently error messages have to choose between providing information and being robust, as evaluating all (or more) of a bad value can cause a secondary exception that's printed as if it's the only error. Here we can choose to ignore that and print the parts that don't throw.
    • mkBold = c: { _type = "pretty"; bold = true; content = c; }: render content in bold (ANSI highlight) in addition to any other styles
    • mkLine = list: { _type = "pretty"; line = list; }: render a concatenation of pretty items and strings
    • mkCode = c: { _type = "pretty"; code = true; }: render as code (MVP: no color + bold). A semantic constructor like this is useful for alternative loggers that render to something more powerful than the ANSI terminal.
    • { _type = "fallback"; fallback = c; }, a forward compatibility measure. Render the fallback if the rest of the attrset is not understood. Only Nixpkgs lib needs this constructor.
  • Possible additions, all nice-to-have:
    • Spatial layout. Adopt the primitives of a proven pretty printing library, such as Haskell's wl-pprint with its primitives
    • Change interpolation to produce a _type = "pretty" document when any of the interpolated strings is a document.
    • Hyperlinks (ANSI extension https://en.wikipedia.org/wiki/ANSI_escape_code)
    • File locations as exposed by unsafeGetAttrPos
    • Render mkCode with Nix expression syntax highlighting
    • Evaluation hints for mkPrettyValue: don't traverse too deeply

Backcompat

Doing this through Nixpkgs lib has the advantage that it can add backward compatibility attributes that are ignored by the renderer.

throw / abort

Note the current behavior:

throw { __toString = "foo"; }
error: foo

By adding an appropriate outPath or __toString attribute to these constructors, we can make the messages render themselves as needed. Won't be quite as pretty, but capable of rendering the MVP level constructors.

trace

Old implementations of trace will pretty-print any non-string value. It's impolite to just throw these internal attributes into someone's face, but that's about the worst that will happen. Just avoid trace for a while, until most everyone has a better Nix.

string interpolation

This is not in the MVP part of the proposal, but does suggest a forward compatibility measure.
Although a non-backcompat constructor wouldn't steal any semantics:

nix-repl> "${{ _type = "pretty"; }}"
error: cannot coerce a set to a string

... a version that does support backcompat would break the meaning of some hypothetical expressions

nix-repl> "${{ _type = "pretty"; outPath = "hmm"; }}"    
"hmm"

nix-repl> "${{ _type = "pretty"; __toString = _: "hmm"; }}"
"hmm"

Breaking the interpolation behavior only for values with signature _type = "pretty"; __toString; ... seems acceptable to me, but we can choose not to do it and have an equally useful feature later, when Nixpkgs' minversion progresses beyond the point where the __toString isn't necessary for compatibility anymore.

However, at that point, some code may rely on the interpolation of documents to produce actual strings. To prevent that, we may want to forbid the interpolation of _type = "pretty"; values into strings, as part of the MVP; reserved syntax.

Describe alternatives you've considered

Interpret the throw and trace arguments as markdown. This would be ambiguous, not extensible and doesn't help with spatial layout.

Additional context

❤️ structured data

Priorities

Add 👍 to issues you find important.

@roberth roberth added feature Feature request or proposal error-messages Confusing messages and better diagnostics language The Nix expression language; parser, interpreter, primops, evaluation, etc labels Feb 9, 2023
@roberth roberth changed the title Pretty throw and trace messages that make Nix more robust Structured markup for throw and trace messages that make Nix more robust Nov 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error-messages Confusing messages and better diagnostics feature Feature request or proposal language The Nix expression language; parser, interpreter, primops, evaluation, etc
Projects
None yet
Development

No branches or pull requests

1 participant