antiquotation -> string interpolation

as proposed by @mkaito[1] and @tazjin[2] and discussed with @edolstra
and Nix maintainers

[1]: https://github.com/NixOS/nix.dev/pull/267#issuecomment-1270076332
[2]: https://github.com/NixOS/nix.dev/pull/267#issuecomment-1270201979

Co-authored-by: John Ericson <git@JohnEricson.me>
Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
This commit is contained in:
Valentin Gagarin 2022-11-09 00:31:48 +01:00
parent 80a0f77e49
commit e0c4a95611
7 changed files with 136 additions and 82 deletions

View file

@ -29,6 +29,7 @@
- [Nix Language](language/index.md) - [Nix Language](language/index.md)
- [Data Types](language/values.md) - [Data Types](language/values.md)
- [Language Constructs](language/constructs.md) - [Language Constructs](language/constructs.md)
- [String interpolation](language/string-interpolation.md)
- [Operators](language/operators.md) - [Operators](language/operators.md)
- [Derivations](language/derivations.md) - [Derivations](language/derivations.md)
- [Advanced Attributes](language/advanced-attributes.md) - [Advanced Attributes](language/advanced-attributes.md)

View file

@ -60,7 +60,7 @@
cache](https://cache.nixos.org). cache](https://cache.nixos.org).
- [store path]{#gloss-store-path}\ - [store path]{#gloss-store-path}\
The location in the file system of a store object, i.e., an The location of a [store object] in the file system, i.e., an
immediate child of the Nix store directory. immediate child of the Nix store directory.
Example: `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1` Example: `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1`
@ -178,3 +178,12 @@
- [`ε`]{#gloss-epsilon}\ - [`ε`]{#gloss-epsilon}\
The epsilon symbol. In the context of a package, this means the version is empty. More precisely, the derivation does not have a version attribute. The epsilon symbol. In the context of a package, this means the version is empty. More precisely, the derivation does not have a version attribute.
- [string interpolation]{#gloss-string-interpolation}\
Expanding expressions enclosed in `${ }` within a [string], [path], or [attribute name].
See [String interpolation](./language/string-interpolation.md) for details.
[string]: ./language/values.md#type-string
[path]: ./language/values.md#type-path
[attribute name]: ./language/values.md#attribute-set

View file

@ -0,0 +1,82 @@
# 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).
Such a string is an *interpolated string*, and an expression inside is an *interpolated expression*.
Interpolated expressions must evaluate to one of the following:
- a [string]
- a [path]
- a [derivation]
[string]: ./values.md#type-string
[path]: ./values.md#type-path
[attribute name]: ./values.md#attribute-set
[derivation]: ../glossary.md#gloss-derivation
## Examples
### String
Rather than writing
```nix
"--with-freetype2-library=" + freetype + "/lib"
```
(where `freetype` is a [derivation]), you can instead write
```nix
"--with-freetype2-library=${freetype}/lib"
```
The latter is automatically translated to the former.
A more complicated example (from the Nix expression for [Qt](http://www.trolltech.com/products/qt)):
```nix
configureFlags = "
-system-zlib -system-libpng -system-libjpeg
${if openglSupport then "-dlopen-opengl
-L${mesa}/lib -I${mesa}/include
-L${libXmu}/lib -I${libXmu}/include" else ""}
${if threadSupport then "-thread" else "-no-thread"}
";
```
Note that Nix expressions and strings can be arbitrarily nested;
in this case the outer string contains various interpolated expressions that themselves contain strings (e.g., `"-thread"`), some of which in turn contain interpolated expressions (e.g., `${mesa}`).
### Path
Rather than writing
```nix
./. + "/" + foo + "-" + bar + ".nix"
```
or
```nix
./. + "/${foo}-${bar}.nix"
```
you can instead write
```nix
./${foo}-${bar}.nix
```
### Attribute name
Attribute names can be created dynamically with string interpolation:
```nix
let name = "foo"; in
{
${name} = "bar";
}
```
{ foo = "bar"; }

View file

@ -13,41 +13,9 @@
returns and tabs can be written as `\n`, `\r` and `\t`, returns and tabs can be written as `\n`, `\r` and `\t`,
respectively. respectively.
You can include the result of an expression into a string by You can include the results of other expressions into a string by enclosing them in `${ }`, a feature known as [string interpolation].
enclosing it in `${...}`, a feature known as *antiquotation*. The
enclosed expression must evaluate to something that can be coerced
into a string (meaning that it must be a string, a path, or a
derivation). For instance, rather than writing
```nix [string interpolation]: ./string-interpolation.md
"--with-freetype2-library=" + freetype + "/lib"
```
(where `freetype` is a derivation), you can instead write the more
natural
```nix
"--with-freetype2-library=${freetype}/lib"
```
The latter is automatically translated to the former. A more
complicated example (from the Nix expression for
[Qt](http://www.trolltech.com/products/qt)):
```nix
configureFlags = "
-system-zlib -system-libpng -system-libjpeg
${if openglSupport then "-dlopen-opengl
-L${mesa}/lib -I${mesa}/include
-L${libXmu}/lib -I${libXmu}/include" else ""}
${if threadSupport then "-thread" else "-no-thread"}
";
```
Note that Nix expressions and strings can be arbitrarily nested; in
this case the outer string contains various antiquotations that
themselves contain strings (e.g., `"-thread"`), some of which in
turn contain expressions (e.g., `${mesa}`).
The second way to write string literals is as an *indented string*, The second way to write string literals is as an *indented string*,
which is enclosed between pairs of *double single-quotes*, like so: which is enclosed between pairs of *double single-quotes*, like so:
@ -75,7 +43,7 @@
Note that the whitespace and newline following the opening `''` is Note that the whitespace and newline following the opening `''` is
ignored if there is no non-whitespace text on the initial line. ignored if there is no non-whitespace text on the initial line.
Antiquotation (`${expr}`) is supported in indented strings. Indented strings support [string interpolation].
Since `${` and `''` have special meaning in indented strings, you Since `${` and `''` have special meaning in indented strings, you
need a way to quote them. `$` can be escaped by prefixing it with need a way to quote them. `$` can be escaped by prefixing it with
@ -143,26 +111,23 @@
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.
Antiquotation is supported in any paths except those in angle brackets. 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].
`./${foo}-${bar}.nix` is a more convenient way of writing
`./. + "/" + foo + "-" + bar + ".nix"` or `./. + "/${foo}-${bar}.nix"`. At
least one slash must appear *before* any antiquotations for this to be
recognized as a path. `a.${foo}/b.${bar}` is a syntactically valid division
operation. `./a.${foo}/b.${bar}` is a path.
When a path appears in an antiquotation, and is thus coerced into a string, [store path]: ../glossary.md#gloss-store-path
the path is first copied into the Nix store and the resulting string is [store object]: ../glossary.md#gloss-store-object
the Nix store path. For instance `"${./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 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"`.
_unchanged_ during the course of the Nix expression evaluation.
If you for example antiquote a file path during a `nix repl` session, and Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression.
then later in the same session, after having changed the file contents, For example, assume you used a file path in an interpolated string during a `nix repl` session.
evaluate the antiquotation with the file path again, then Nix will still 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.
return the first store path. It will _not_ reread the file contents to
produce a different Nix store path. Paths themselves, except those in angle brackets (`< >`), support [string interpolation].
At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path.
`a.${foo}/b.${bar}` is a syntactically valid division operation.
`./a.${foo}/b.${bar}` is a path.
- <a id="type-boolean" href="#type-boolean">Boolean</a> - <a id="type-boolean" href="#type-boolean">Boolean</a>
@ -235,23 +200,33 @@ will evaluate to `"Xyzzy"` because there is no `c` attribute in the set.
You can use arbitrary double-quoted strings as attribute names: You can use arbitrary double-quoted strings as attribute names:
```nix ```nix
{ "foo ${bar}" = 123; "nix-1.0" = 456; }."foo ${bar}" { "$!@#?" = 123; }."$!@#?"
``` ```
This will evaluate to `123` (Assuming `bar` is antiquotable). In the
case where an attribute name is just a single antiquotation, the quotes
can be dropped:
```nix ```nix
{ foo = 123; }.${bar} or 456 let bar = "bar";
{ "foo ${bar}" = 123; }."foo ${bar}"
``` ```
This will evaluate to `123` if `bar` evaluates to `"foo"` when coerced Both will evaluate to `123`.
to a string and `456` otherwise (again assuming `bar` is antiquotable).
Attribute names support [string interpolation]:
```nix
let bar = "foo"; in
{ foo = 123; }.${bar}
```
```nix
let bar = "foo"; in
{ ${bar} = 123; }.foo
```
Both will evaluate to `123`.
In the special case where an attribute name inside of a set declaration In the special case where an attribute name inside of a set declaration
evaluates to `null` (which is normally an error, as `null` is not evaluates to `null` (which is normally an error, as `null` cannot be coerced to
antiquotable), that attribute is simply not added to the set: a string), that attribute is simply not added to the set:
```nix ```nix
{ ${if foo then "bar" else null} = true; } { ${if foo then "bar" else null} = true; }

View file

@ -15,19 +15,6 @@
# NIX_PATH=nixpkgs=flake:nixpkgs nix-build '<nixpkgs>' -A hello # NIX_PATH=nixpkgs=flake:nixpkgs nix-build '<nixpkgs>' -A hello
``` ```
* Allow explicitly selecting outputs in a store derivation installable, just like we can do with other sorts of installables. * Instead of "antiquotation", the more common term [string interpolation](../language/string-interpolation.md) is now used consistently.
For example, Historical release notes were not changed.
```shell-session
$ nix-build /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^dev`
```
now works just as
```shell-session
$ nix-build glibc^dev`
```
does already.
* On Linux, `nix develop` now sets the
[*personality*](https://man7.org/linux/man-pages/man2/personality.2.html)
for the development shell in the same way as the actual build of the
derivation. This makes shells for `i686-linux` derivations work
correctly on `x86_64-linux`.

View file

@ -1930,8 +1930,8 @@ static RegisterPrimOp primop_toFile({
"; ";
``` ```
Note that `${configFile}` is an Note that `${configFile}` is a
[antiquotation](language-values.md), so the result of the [string interpolation](language/values.md#type-string), so the result of the
expression `configFile` expression `configFile`
(i.e., a path like `/nix/store/m7p7jfny445k...-foo.conf`) will be (i.e., a path like `/nix/store/m7p7jfny445k...-foo.conf`) will be
spliced into the resulting string. spliced into the resulting string.

View file

@ -110,7 +110,7 @@ let
And finally to interpret \n etc. as in a string: ''\n, ''\r, ''\t. And finally to interpret \n etc. as in a string: ''\n, ''\r, ''\t.
''; '';
# Regression test: antiquotation in '${x}' should work, but didn't. # Regression test: string interpolation in '${x}' should work, but didn't.
s15 = let x = "bla"; in '' s15 = let x = "bla"; in ''
foo foo
'${x}' '${x}'