TL;DR: If you rely on <CODE>
, <LAMBDA>
, or similar in nix-instantiate --eval
’s output syntax, chime in on this PR or in this thread. If you have thoughts on what parts of Nix should be considered a “stable API” and what guarantees a “stable API” should uphold, discuss here. Read on for context.
In service of extending the cannot coerce a set to a string
error messages to include the relevant value, I was asked to write a routine to print Nix values in a limited manner (i.e., limiting output to a dozen attributes, to avoid printing huge data structures).
Nix actually has two separate functions for printing values right now: the one used in nix-instantiate --eval
output (which prints unevaluated thunks as <CODE>
and functions as <LAMBDA>
), and the one used in nix repl
(which forces values and prints functions as «lambda @ lists.nix:139:5»
). The nix repl
printer starts with a comment saying it was originally copy-pasted from the nix-instantiate
printer, so I figured this was a good time to unify the two printing routines into one.
I chose to base the new printer’s styling on the nix repl
printer for a few reasons. First, the nix-instantiate
output is ambiguous and not machine-consumable, because <CODE>
is valid Nix path syntax:
$ nix-instantiate --eval --expr '{ foo = <CODE>; }'
{ foo = <CODE>; }
$ nix-instantiate --eval --expr '{ foo = {}; }'
{ foo = <CODE>; }
(In fact, @edolstra himself cited this exact reasoning when changing <REPEAT>
to «repeated»
in nix-instantiate
’s output.)
For machine consumption where preserving laziness is important, the nix-instantiate --xml
output makes it explicit that the value isn’t a path:
$ nix-instantiate --eval --xml --expr '{ foo = <CODE>; }'
<?xml version='1.0' encoding='utf-8'?>
<expr>
<attrs>
<attr column="3" line="1" name="foo">
<unevaluated />
</attr>
</attrs>
</expr>
Second, the nix-instantiate
output is inconsistent. Where nix repl
’s output makes it clear that text between «chevrons»
is “meta-output” and not a literal Nix expression, nix-instantiate
uses multiple different conventions: <CODE>
, <LAMBDA>
, and others use angle brackets, «repeated»
and «potential infinite recursion»
use chevrons, and there’s also (nullptr)
which uses parenthesis.
Finally, nix-instantiate
output is uninformative. Where nix repl
prints source locations like «lambda @ lists.nix:139:5»
, nix-instantiate
just prints <LAMBDA>
. (Here’s a user complaining about this in 2019.)
With all this in mind, I was surprised when my PR was rejected due to changing these parts of the non-strict nix-instantiate
output. Surely nobody would consider <CODE>
to be for machine consumption? Some members of the Nix team agree — on Matrix, @thufschmitt said that he “certainly wouldn’t recommend” depending on the output, and @tomberek said that “I believe we’ve done plenty of due-diligence for this particular proposal and I am inclined to proceed”.
This issue seems to be partially precipitated by the absence of a written compatibility policy. On Matrix, Nix team members consistently say that Nix is “stable”, but the confines of that stability aren’t defined, so my change was rejected with no rationale more detailed than “I have to be conservative about changes in behavior” (as @roberth said to me in a DM).
However, so far, we haven’t discovered any use case for the <CODE>
output that wouldn’t be better-served by using the unambiguous XML output, and we’ve only found one current user of the <CODE>
output.
@sternenseemann wants to rely on the <CODE>
output for testing laziness. In the Tvix IRC channel, he agreed that “maybe XML is the solution in the end” for this use case, but on the PR itself he requested that the output not change so that the test suite in the Nix repository could be used as an ad-hoc language spec. I’m not very moved by this proposal; Nix’s development shouldn’t be shaped by a rewrite project that’s not yet production-ready, and “checking the same tests with multiple versions of C++ Nix” seems similarly absurd to me — the tests are intrinsically tied to the version of the code they’re committed with, and the tests and code change in lockstep, in response to each other. If the Nix test suite was the stable API, we could never improve Nix’s error messages!
A search for usages of the <CODE>
output on GitHub found a project called nix-browse, last updated in 2017, with a README that simply states “Nothing to see here yet…”.
Those are the only users of the <CODE>
output in nix-instantiate
I’ve been able to find. Do you rely on <CODE>
or <LAMBDA>
in nix-instantiate --eval
output? If so, please chime in with your use case here or on the PR. I don’t want to break anyone’s use case if the alternatives (e.g. XML output) aren’t viable.