Skip to content

Commit

Permalink
primops: make nature of foldl' strictness clearer
Browse files Browse the repository at this point in the history
* Clarify the documentation of foldl': That the arguments are forced
  before application (?) of `op` is necessarily true. What is important
  to stress is that we force every application of `op`, even when the
  value turns out to be unused.

* Move the example before the comment about strictness to make it less
  confusing: It is a general example and doesn't really showcase anything
  about foldl' strictness.

* Add test cases which nail down aspects of foldl' strictness:
  * The initial accumulator value is not forced unconditionally.
  * Applications of op are forced.
  * The list elements are not forced unconditionally.
  • Loading branch information
sternenseemann committed Oct 16, 2022
1 parent ac0fb38 commit d0f2da2
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 3 deletions.
6 changes: 3 additions & 3 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2908,9 +2908,9 @@ static RegisterPrimOp primop_foldlStrict({
.doc = R"(
Reduce a list by applying a binary operator, from left to right,
e.g. `foldl' op nul [x0 x1 x2 ...] = op (op (op nul x0) x1) x2)
...`. The operator is applied strictly, i.e., its arguments are
evaluated first. For example, `foldl' (x: y: x + y) 0 [1 2 3]`
evaluates to 6.
...`. For example, `foldl' (x: y: x + y) 0 [1 2 3]` evaluates to 6.
The return value of each application of `op` is evaluated immediately,
even for intermediate values.
)",
.fun = prim_foldlStrict,
});
Expand Down
5 changes: 5 additions & 0 deletions tests/lang/eval-fail-foldlStrict-strict-op-application.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Tests that the result of applying op is forced even if the value is never used
builtins.foldl'
(_: f: f null)
null
[ (_: throw "Not the final value, but is still forced!") (_: 23) ]
1 change: 1 addition & 0 deletions tests/lang/eval-okay-foldlStrict-lazy-elements.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
42
9 changes: 9 additions & 0 deletions tests/lang/eval-okay-foldlStrict-lazy-elements.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Tests that the rhs argument of op is not forced unconditionally
let
lst = builtins.foldl'
(acc: x: acc ++ [ x ])
[ ]
[ 42 (throw "this shouldn't be evaluated") ];
in

builtins.head lst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
42
6 changes: 6 additions & 0 deletions tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Checks that the nul value for the accumulator is not forced unconditionally.
# Some languages provide a foldl' that is strict in this argument, but Nix does not.
builtins.foldl'
(_: x: x)
(throw "This is never forced")
[ "but the results of applying op are" 42 ]

0 comments on commit d0f2da2

Please sign in to comment.