Repository: tweag/nickel
Pull request #1927 (2024-05-25 21:41:44)

Add stdlib function array.at_or
This is to return value at given position of array or default in case provided position is out of bound.

yannham: @olorin37 those line numbers in tests are a bit annoying. You need to run `cargo insta review` and accept the new version of the snapshots to make the tests pass (by the way, it's probably quicker for iteration to run `cargo tests` locally than to wait for the CI which isn't the fastest). If you don't have the bandwidth I can also just take over from here, if you'd prefer.
olorin37: Yep, I already figure out staff relatet to `cargo insta`... But now there is a different problem... I couldn't understand it, is something what tests snippets in manual...
olorin37: Hmm, i see that those line numbers still visible in check on CI... Locally I've already solve this ... Maybe I forgot to push last changes, as I hit next problem, but it would be strange... As I feel that I pushed those insta review changes... Maybe there is a diff between my env and CI which causes this, I'll try align CI to my env...
yannham: It's probably the same problem, but for manual snippets. The CI tests all manual snippets as well, including their output - for now it's not smart enough to strip unimportant information like line numbers. So changing the stdlib can always be a bit painful on that front, if you shuffle line numbers, you have to fix the corresponding excerpt which includes full error messages in the manual. Hopefully the error message should give you the diff of what line number you need to change.

Long term, it would probably be nicer to just strip stdlib line numbers from the output - we already do something similar in snapshots test to strip the local file paths from error snapshots.
olorin37: I got following error:

```
> std.array.filter (fun x => if x % 2 == 0 then x else null) [1,2,3,4,5,6]

thread 'check_manual_snippets_doc_manual_typing_md' panicked at core/tests/manual/main.rs:136:49:
error: contract broken by the caller of `filter`
    ┌─ <stdlib/std.ncl>:349:25
    │
349 │       : forall a. (a -> Bool) -> Array a -> Array a
    │                         ---- expected return type of a function provided by the caller
    │
    ┌─ <repl-input-6>:1:55
    │
  1 │  std.array.filter (fun x => if x % 2 == 0 then x else null) [1,2,3,4,5,6]
    │                                                       ---- evaluated to this expression
 was expected to be a prefix of error: contract broken by the caller of `filter`
    ┌─ <stdlib/std.ncl>:373:25
    │
373 │       : forall a. (a -> Bool) -> Array a -> Array a
    │                         ---- expected return type of a function provided by the caller
    │
    ┌─ <repl-input-6>:1:55
    │
  1 │  std.array.filter (fun x => if x % 2 == 0 then x else null) [1,2,3,4,5,6]
    │                                                       ---- evaluated to this expression

note:
  ┌─ <repl-input-6>:1:2
  │
1 │  std.array.filter (fun x => if x % 2 == 0 then x else null) [1,2,3,4,5,6]
  │  ------------------------------------------------------------------------ (1) calling filter


note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    check_manual_snippets_doc_manual_typing_md

test result: FAILED. 9 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.31s
```
olorin37: @yannham OK, so now on CI you can see same problem which I have. I do not know why filter function somewhere is broken now?
vkleen: The `filter` is a red herring. The problem is that the line number in `std.ncl` where filter is defined has changed, and the test checks the output of Nickel evaluations in the manual verbatim. In particular, it checks the error messages and the line number appears in them.
olorin37: Nice, OK. So, how to change those line numbers? Because `cargo insta review` doesn't ask for accepting filter snippe?
vkleen: For the manual I think you need to change them manually, they don't go via `cargo insta`. I.e. just edit `doc/manual/typing.md` (in this case).
olorin37: Nice, OK. So, how to change those line numbers? Because `cargo insta review` doesn't ask for accepting filter snippe?
olorin37: OK, so now you can freely review and approve it it is OK. @yannham or @vkleen. Thanks for guidance!

Patch:
diff --git a/core/stdlib/std.ncl b/core/stdlib/std.ncl
index 2b2bdd39..43c11c12 100644
--- a/core/stdlib/std.ncl
+++ b/core/stdlib/std.ncl
@@ -172,6 +172,30 @@
         "%
       = fun n l => %elem_at% l n,
 
+    at_or
+      : forall a. Number -> a -> Array a -> a
+      | std.number.Nat -> Dyn
+      | doc m%"
+          Retrieves the n-th element from an array, with indices starting at 0
+          or the provided value if the index is greater than the length of the
+          array.
+
+          # Examples
+
+          ```nickel
+          std.array.at_or 3 "default" [ "zero", "one", "two", "three" ] =>
+            "three"
+
+          std.array.at_or 3 "default" [ "zero", "one" ] =>
+            "default"
+          ```
+        "%
+      = fun n default_value array =>
+        if n < %length% array then
+          %elem_at% array n
+        else
+          default_value,
+
     concat
       : forall a. Array a -> Array a -> Array a
       | doc m%"
diff --git a/doc/manual/typing.md b/doc/manual/typing.md
index 9083faba..f1b67a2b 100644
--- a/doc/manual/typing.md
+++ b/doc/manual/typing.md
@@ -588,9 +588,9 @@ calling to the statically typed `std.array.filter` from dynamically typed code:
 ```nickel #repl
 > std.array.filter (fun x => if x % 2 == 0 then x else null) [1,2,3,4,5,6]
 error: contract broken by the caller of `filter`
-    ┌─ <stdlib/std.ncl>:349:25
+    ┌─ <stdlib/std.ncl>:373:25
     │
-349 │       : forall a. (a -> Bool) -> Array a -> Array a
+373 │       : forall a. (a -> Bool) -> Array a -> Array a
     │                         ---- expected return type of a function provided by the caller
     │
     ┌─ <repl-input-6>:1:55


Test patch:
diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap
index cddf2ea7..dcd64061 100644
--- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap
+++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap
@@ -4,9 +4,9 @@ expression: err
 ---
 error: contract broken by the caller of `range`
        invalid range
-    ┌─ <stdlib/std.ncl>:649:9
+    ┌─ <stdlib/std.ncl>:673:9
     │
-649 │       | std.contract.unstable.RangeFun Dyn
+673 │       | std.contract.unstable.RangeFun Dyn
     │         ---------------------------------- expected type
     │
     ┌─ [INPUTS_PATH]/errors/array_range_reversed_indices.ncl:3:19
@@ -21,5 +21,3 @@ note:
   │
 3 │ std.array.range 1 0
   │ ------------------- (1) calling range
-
-
diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap
index caba5f75..b80bb099 100644
--- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap
+++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap
@@ -4,9 +4,9 @@ expression: err
 ---
 error: contract broken by the caller of `range_step`
        invalid range step
-    ┌─ <stdlib/std.ncl>:624:9
+    ┌─ <stdlib/std.ncl>:648:9
     │
-624 │       | std.contract.unstable.RangeFun (std.contract.unstable.RangeStep -> Dyn)
+648 │       | std.contract.unstable.RangeFun (std.contract.unstable.RangeStep -> Dyn)
     │         ----------------------------------------------------------------------- expected type
     │
     ┌─ [INPUTS_PATH]/errors/array_range_step_negative_step.ncl:3:27
@@ -26,5 +26,3 @@ note:
   │
 3 │ std.array.range_step 0 10 (-1)
   │ ------------------------------ (1) calling range_step
-
-