Merge pull request #9116 from fricklerhandwerk/doc-import

reword and reformat description of `builtins.import`
This commit is contained in:
John Ericson 2023-10-09 10:26:41 -04:00 committed by GitHub
commit 67eddc05ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 194 additions and 73 deletions

View file

@ -1,19 +1,12 @@
# String interpolation # String interpolation
String interpolation is a language feature where a [string], [path], or [attribute name] can contain expressions enclosed in `${ }` (dollar-sign with curly brackets). String interpolation is a language feature where a [string], [path], or [attribute name][attribute set] can contain expressions enclosed in `${ }` (dollar-sign with curly brackets).
Such a string is an *interpolated string*, and an expression inside is an *interpolated expression*. Such a construct is called *interpolated string*, and the expression inside is an [interpolated expression](#interpolated-expression).
Interpolated expressions must evaluate to one of the following:
- a [string]
- a [path]
- a [derivation]
[string]: ./values.md#type-string [string]: ./values.md#type-string
[path]: ./values.md#type-path [path]: ./values.md#type-path
[attribute name]: ./values.md#attribute-set [attribute set]: ./values.md#attribute-set
[derivation]: ../glossary.md#gloss-derivation
## Examples ## Examples
@ -70,13 +63,136 @@ you can instead write
### Attribute name ### Attribute name
Attribute names can be created dynamically with string interpolation: <!--
FIXME: these examples are redundant with the main page on attribute sets.
figure out what to do about that
-->
```nix Attribute names can be interpolated strings.
let name = "foo"; in
{
${name} = "bar";
}
```
{ foo = "bar"; } > **Example**
>
> ```nix
> let name = "foo"; in
> { ${name} = 123; }
> ```
>
> { foo = 123; }
Attributes can be selected with interpolated strings.
> **Example**
>
> ```nix
> let name = "foo"; in
> { foo = 123; }.${name}
> ```
>
> 123
# Interpolated expression
An interpolated expression must evaluate to one of the following:
- a [string]
- a [path]
- an [attribute set] that has a `__toString` attribute or an `outPath` attribute
- `__toString` must be a function that takes the attribute set itself and returns a string
- `outPath` must be a string
This includes [derivations](./derivations.md) or [flake inputs](@docroot@/command-ref/new-cli/nix3-flake.md#flake-inputs) (experimental).
A string interpolates to itself.
A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](../glossary.md#gloss-store-object).
[store path]: ../glossary.md#gloss-store-path
> **Example**
>
> ```console
> $ mkdir foo
> ```
>
> Reference the empty directory in an interpolated expression:
>
> ```nix
> "${./foo}"
> ```
>
> "/nix/store/2hhl2nz5v0khbn06ys82nrk99aa1xxdw-foo"
A derivation interpolates to the [store path] of its first [output](./derivations.md#attr-outputs).
> **Example**
>
> ```nix
> let
> pkgs = import <nixpkgs> {};
> in
> "${pkgs.hello}"
> ```
>
> "/nix/store/4xpfqf29z4m8vbhrqcz064wfmb46w5r7-hello-2.12.1"
An attribute set interpolates to the return value of the function in the `__toString` applied to the attribute set itself.
> **Example**
>
> ```nix
> let
> a = {
> value = 1;
> __toString = self: toString (self.value + 1);
> };
> in
> "${a}"
> ```
>
> "2"
An attribute set also interpolates to the value of its `outPath` attribute.
> **Example**
>
> ```nix
> let
> a = { outPath = "foo"; };
> in
> "${a}"
> ```
>
> "foo"
If both `__toString` and `outPath` are present in an attribute set, `__toString` takes precedence.
> **Example**
>
> ```nix
> let
> a = { __toString = _: "yes"; outPath = throw "no"; };
> in
> "${a}"
> ```
>
> "yes"
If neither is present, an error is thrown.
> **Example**
>
> ```nix
> let
> a = {};
> in
> "${a}"
> ```
>
> error: cannot coerce a set to a string
>
> at «string»:4:2:
>
> 3| in
> 4| "${a}"
> | ^

View file

@ -112,18 +112,16 @@
environment variable `NIX_PATH` will be searched for the given file environment variable `NIX_PATH` will be searched for the given file
or directory name. or directory name.
When an [interpolated string][string interpolation] evaluates to a path, the path is first copied into the Nix store and the resulting string is the [store path] of the newly created [store object].
[store path]: ../glossary.md#gloss-store-path
[store object]: ../glossary.md#gloss-store-object
For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` in the current directory to be copied into the Nix store and result in the string `"/nix/store/<hash>-foo.txt"`. For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` in the current directory to be copied into the Nix store and result in the string `"/nix/store/<hash>-foo.txt"`.
Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression.
For example, assume you used a file path in an interpolated string during a `nix repl` session. For example, assume you used a file path in an interpolated string during a `nix repl` session.
Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new store path, since Nix might not re-read the file contents. Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents.
Paths themselves, except those in angle brackets (`< >`), support [string interpolation]. [store path]: ../glossary.md#gloss-store-path
Paths, except those in angle brackets (`< >`), support [string interpolation] and can be used in [interpolated expressions].
[interpolated expressions]: ./string-interpolation.md#interpolated-expressions
At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path.

View file

@ -258,64 +258,71 @@ static RegisterPrimOp primop_import({
.args = {"path"}, .args = {"path"},
// TODO turn "normal path values" into link below // TODO turn "normal path values" into link below
.doc = R"( .doc = R"(
Load, parse and return the Nix expression in the file *path*. Load, parse, and return the Nix expression in the file *path*.
The value *path* can be a path, a string, or an attribute set with an
`__toString` attribute or a `outPath` attribute (as derivations or flake
inputs typically have).
If *path* is a directory, the file `default.nix` in that directory
is loaded.
Evaluation aborts if the file doesnt exist or contains
an incorrect Nix expression. `import` implements Nixs module
system: you can put any Nix expression (such as a set or a
function) in a separate file, and use it from Nix expressions in
other files.
> **Note** > **Note**
> >
> Unlike some languages, `import` is a regular function in Nix. > Unlike some languages, `import` is a regular function in Nix.
> Paths using the angle bracket syntax (e.g., `import` *\<foo\>*)
> are normal [path values](@docroot@/language/values.md#type-path).
A Nix expression loaded by `import` must not contain any *free The *path* argument must meet the same criteria as an [interpolated expression](@docroot@/language/string-interpolation.md#interpolated-expression).
variables* (identifiers that are not defined in the Nix expression
itself and are not built-in). Therefore, it cannot refer to
variables that are in scope at the call site. For instance, if you
have a calling expression
```nix If *path* is a directory, the file `default.nix` in that directory is used if it exists.
rec {
x = 123;
y = import ./foo.nix;
}
```
then the following `foo.nix` will give an error: > **Example**
>
> ```console
> $ echo 123 > default.nix
> ```
>
> Import `default.nix` from the current directory.
>
> ```nix
> import ./.
> ```
>
> 123
```nix Evaluation aborts if the file doesnt exist or contains an invalid Nix expression.
x + 456
```
since `x` is not in scope in `foo.nix`. If you want `x` to be A Nix expression loaded by `import` must not contain any *free variables*, that is, identifiers that are not defined in the Nix expression itself and are not built-in.
available in `foo.nix`, you should pass it as a function argument: Therefore, it cannot refer to variables that are in scope at the call site.
```nix > **Example**
rec { >
x = 123; > If you have a calling expression
y = import ./foo.nix x; >
} > ```nix
``` > rec {
> x = 123;
and > y = import ./foo.nix;
> }
```nix > ```
x: x + 456 >
``` > then the following `foo.nix` will give an error:
>
(The function argument doesnt have to be called `x` in `foo.nix`; > ```nix
any name would work.) > # foo.nix
> x + 456
> ```
>
> since `x` is not in scope in `foo.nix`.
> If you want `x` to be available in `foo.nix`, pass it as a function argument:
>
> ```nix
> rec {
> x = 123;
> y = import ./foo.nix x;
> }
> ```
>
> and
>
> ```nix
> # foo.nix
> x: x + 456
> ```
>
> The function argument doesnt have to be called `x` in `foo.nix`; any name would work.
)", )",
.fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v)
{ {