fix(tvix/eval): bring foldl' strictness in line with C++ Nix

Working on https://github.com/NixOS/nix/pull/7158, I discovered that C++
Nix actually is strict in the accumulator, just not in the first value.
This seems due to the fact that in the C++ evaluator, function calls
don't seem to be thunked unconditionally and foldl' just elects not to
wrap it in a thunk (don't quote me on this summary, even though it seems
to line up with the code for primop_foldlStrict and testable behavior).

It doesn't seem worth it to risk breaking the odd Nix expression just to
be strict in one more value per invocation of foldl' (i.e. the initial
accumulator value `nul`), so let's match the existing C++ Nix behavior
here.

Change-Id: If59e62271a90d97cb440f0ca72a58ec7840d1690
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7022
Autosubmit: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
sterni 2022-10-15 15:52:37 +02:00 committed by clbot
parent caf9cbf711
commit f3c27f1717
7 changed files with 16 additions and 2 deletions

View file

@ -17,7 +17,6 @@ maybe to get rid of the behavior in all implementations for good. Below is an
* [Behaviour of nested attribute sets depends on definition order][i7111] * [Behaviour of nested attribute sets depends on definition order][i7111]
* [Partially constructed attribute sets are observable during dynamic attr names construction][i7012] * [Partially constructed attribute sets are observable during dynamic attr names construction][i7012]
* [Nix parsers merges multiple attribute set literals for the same key incorrectly depending on definition order](i7115) * [Nix parsers merges multiple attribute set literals for the same key incorrectly depending on definition order](i7115)
* [builtins.foldl' doesn't seem to be strict](p7158)
On the other hand, there is behavior that seems to violate one's expectation On the other hand, there is behavior that seems to violate one's expectation
about the language at first, but has good enough reasons from an implementor's about the language at first, but has good enough reasons from an implementor's
@ -35,6 +34,12 @@ perspective to keep them:
implementation already merges at parse time, making nested attribute keys implementation already merges at parse time, making nested attribute keys
syntactic sugar effectively. syntactic sugar effectively.
Other behavior is just odd, surprising or underdocumented:
* `builtins.foldl'` doesn't force the initial accumulator (but all other
intermediate accumulator values), differing from e.g. Haskell, see
the [relevant PR discussion](p7158).
[i7111]: https://github.com/NixOS/nix/issues/7111 [i7111]: https://github.com/NixOS/nix/issues/7111
[i7012]: https://github.com/NixOS/nix/issues/7012 [i7012]: https://github.com/NixOS/nix/issues/7012
[i7115]: https://github.com/NixOS/nix/issues/7115 [i7115]: https://github.com/NixOS/nix/issues/7115

View file

@ -264,8 +264,8 @@ fn pure_builtins() -> Vec<Builtin> {
let mut res = args.pop().unwrap(); let mut res = args.pop().unwrap();
let op = args.pop().unwrap(); let op = args.pop().unwrap();
for val in list { for val in list {
res.force(vm)?;
res = vm.call_with(&op, [val, res])?; res = vm.call_with(&op, [val, res])?;
res.force(vm)?;
} }
Ok(res) Ok(res)

View file

@ -0,0 +1,4 @@
builtins.foldl'
(_: f: f null)
(throw "This doesn't explode")
[ (_: throw "Not the final value, but is still forced!") (_: 23) ]

View file

@ -0,0 +1,4 @@
builtins.foldl'
(_: x: x)
(throw "This is never forced")
[ "but the results of applying op are" 42 ]