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

PoC for RFC145: dynamic documentation for lambdas #8778

Closed
wants to merge 2 commits into from

Conversation

sternenseemann
Copy link
Member

@sternenseemann sternenseemann commented Aug 3, 2023

Ref. NixOS/rfcs#145.

The first commit is a faithful rebase of #1652. The later commits change the approach to conform with our more modern ideas and also add everything we'd need to hypothetically generate nixpkgs' documentation using a pure Nix expression.

Currently unfinished:

Context

@github-actions github-actions bot added with-tests Issues related to testing. PRs with tests have some priority repl The Read Eval Print Loop, "nix repl" command and debugger labels Aug 3, 2023
@hsjobeki
Copy link
Contributor

hsjobeki commented Sep 12, 2023

I checked out your branch, added two builtins: (Thanks @roberth; @asymmetric for your help, Thx @sternenseemann)

  • unsafeGetAttrDoc
  • unsafeGetLambdaDoc

-> hsjobeki/nix/lambda-docstring

Almost all test cases are working, and we'll bootstrap a user guide with a bunch of examples here this week.

To support the ( last?) edge case we need: #8968.

For record, this edge case beeing:

A partially applied lambda, where the docstring is behind a pattern argument ({ attr ? <expr>, ...}@arg)
This is arbitrarily complex with the current solution, to solve this we need access to the parent node in the AST.

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Five years from now we'll look back on builtins.unsafeGetLambdaDoc the way we look back on Nix function equality today.

... yeah but it seemed like such a useful trick at the time!

Comment on lines +2443 to +2463
void prim_unsafeGetLambdaDoc(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos, "while evaluating the first argument to builtins.unsafeGetLambdaDoc");

Pos funPos = state.positions[args[0]->lambda.fun->pos];

Comment::Doc doc = Comment::lookupDoc(funPos);

if(doc.comment.empty()) {
v.mkNull();
} else {
v.mkString(doc.comment);
}
}

static RegisterPrimOp primop_unsafeGetLambdaDoc(PrimOp {
.name = "__unsafeGetLambdaDoc",
.arity = 1,
.fun = prim_unsafeGetLambdaDoc,
});

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be behind an --experimental flag to prevent it from being used in nixpkgs.

@hsjobeki
Copy link
Contributor

hsjobeki commented Nov 18, 2023

I do know it is a bad implementation. We should definetly not merge it. It was just for playing around with position tracking.

@sternenseemann can you close this PR to prevent people from thinking we should merge it.

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do know it is a bad implementation. We should definetly not merge it. It was just for playing around with position tracking.

Oh I don't think so!

I just want to make sure we don't make promises that can't be kept in the future.

What do you think about the change below? It basically requires that you must use nix-instantiate with --eval and without --read-write-mode in order to introspect on documentation comments. This means you can't produce derivations, but you can do anything else, including dumping out JSON which other tools then consume.

Would this conflict with any of your envisioned use cases?

If so I'd like to talk about those use cases to understand them better.

My two main concerns are:

  1. Small concern: philosophical. Comments are supposed to be... comments. Like, changing them doesn't affect the execution of the program. This is a minor concern though.

  2. Bigger concern: making sure that we can "compile-out" doc comment support for use on really big build clusters, like Hydra.

If we disallow writing to the store in doc-comment-introspection-mode, then we don't have to worry about the performance impact of doc-comments at all. For high-performance implementations like tvix we can have a #cfg[doc-comments] (basically Rust's version of #ifdef) and compile the interpreter twice: one version that goes really fast and omits any extra fields needed for tracking doc-comments, and another version that can handle doc-comments that are as rich as you like. By not allowing derivations to be created in doc-comments mode we guarantee that nixpkgs can always be built using the high-performance interpreter.

Then you won't have to worry about performance at all when designing this feature. It should give you a lot more freedom.

@@ -2439,6 +2440,27 @@ static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp {
.fun = prim_unsafeGetAttrPos,
});

void prim_unsafeGetLambdaDoc(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceFunction(*args[0], pos, "while evaluating the first argument to builtins.unsafeGetLambdaDoc");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
state.forceFunction(*args[0], pos, "while evaluating the first argument to builtins.unsafeGetLambdaDoc");
if (!settings.readOnlyMode) {
throw Error("attempted to introspect on documentation comments in derivation-producing mode");
}
state.forceFunction(*args[0], pos, "while evaluating the first argument to builtins.unsafeGetLambdaDoc");

@hsjobeki
Copy link
Contributor

hsjobeki commented Nov 30, 2023

Yes, I think it should be behind some secure flags. Currently, we might need to rethink the idea with the new builtins.

The new idea would be to have that feature as a nix command (e.g. nix doc --file <lib.nix> --expr "lib.add" )
I am not sure about the implications of that. Potential doc building would involve a step to generate e.g. a HashMap<File: Vec<position>> (using rust notation here)
Where each file has a list of expressions where i want to retrieve doc-comments from. (e.g. lib/lists.nix : [ lib.lists.map, lib.lists.concat ...]
Then i can invoke the new command nix doc.

compile the interpreter twice:
If possible i'd like to have the functionality in a new command (possibly) if we can handle the memory layout to be performant without the command.

@ghost
Copy link

ghost commented Nov 30, 2023

Then i can invoke the new command nix doc.

I think that's a great idea. Also the :doc repl command sounds like a great idea too.

After thinking a lot about this, the only place I see trouble would be if we added builtins.unsafeGetDocComment without the settings.readOnlyMode restriction. So, please just keep that in mind if you ever decide to switch back to builtins.unsafeGetDocComment. There's nothing wrong with the builtin; we just need to make sure that it can't affect Hydra builds; that's all.

@roberth
Copy link
Member

roberth commented Dec 18, 2024

Hi @sternenseemann,
Thank you for your work on this.
We have merged an alternate, similar implementation some time ago.
I think we'd like to avoid adding primops that retrieve documentation, because we have alternate, in-derivation solutions for this kind of thing now, and doing it at evaluation time risks ossifying their behavior while we aren't quite sure what the best behavior is e.g. when multiple potential comments exist.
Maybe they could be added at some point, but for now it seems like too great a liability to me.
Closing for now.

@roberth roberth closed this Dec 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
repl The Read Eval Print Loop, "nix repl" command and debugger with-tests Issues related to testing. PRs with tests have some priority
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Inline docstring support
3 participants