Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add iteration index to errors #945

Merged
merged 26 commits into from
Sep 23, 2022
Merged

Add iteration index to errors #945

merged 26 commits into from
Sep 23, 2022

Conversation

hadley
Copy link
Member

@hadley hadley commented Sep 15, 2022

Fixes #929

So far, just a quick proof of concept

To do:

  • Wrap map2 and pmap functions
  • Update C code as below
  • Pass explicit env to check_vector()/r_abort() or move that check into R
  • News bullet

@@ -3,7 +3,13 @@
Code
map_depth(x1, 6, length)
Condition
Error in `map_depth()`:
Error in `.fmap()`:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is verbose, but it's technically correct and possibly helpful.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably fine for this particular function. I'm expecting this sort of errors to be internal errors rather than user errors.

tests/testthat/_snaps/keep.md Outdated Show resolved Hide resolved
@hadley hadley changed the title Add itearation index to errors Add iteration index to errors Sep 15, 2022
@DavisVaughan
Copy link
Member

Noting that when all of the map friends are converted over to use this, on the C side we shouldn't create and overwrite i like this:

purrr/src/map.c

Lines 39 to 42 in 426acdd

// Create variable "i" and map to scalar integer
SEXP i_val = PROTECT(Rf_ScalarInteger(1));
SEXP i = Rf_install("i");
Rf_defineVar(i, i_val, env);

Since it already exists in the function environment through i <- 0L, we should use something like Rf_findVarInFrame() to look it up and modify it directly

Conflicts:
	R/map.R
	tests/testthat/_snaps/map2.md
	tests/testthat/_snaps/modify.md
@DavisVaughan
Copy link
Member

Can we improve on these two cases?

> map(-1:1, stop)
Error in `map()`:
! Computation failed in index 1
Caused by error in `withCallingHandlers()`:
! -1
Run `rlang::last_trace()` to see where the error occurred.
fn <- function(x) {
  if (x == 2) {
    rlang::abort("oh no")
  }
  x
}

map(-1:3, fn)
Error in `map()`:
! Computation failed in index 4
Caused by error in `.f()`:
! oh no
Run `rlang::last_trace()` to see where the error occurred.

R/map.R Outdated Show resolved Hide resolved
@hadley
Copy link
Member Author

hadley commented Sep 21, 2022

@DavisVaughan I think our conclusion was that we can't easily improve on that, right?

library(purrr)

# call. is TRUE by default
map(-1:1, stop)
#> Error in `map()`:
#> ! Computation failed in index 1
#> Caused by error in `withCallingHandlers()`:
#> ! -1

map("a", rlang::abort)
#> Error in `map()`:
#> ! Computation failed in index 1
#> Caused by error in `map()`:
#> ! a

# have lost original name of function
fn <- function(x) {
  if (x == 2) {
    rlang::abort("oh no")
  }
  x
}
map(-1:3, fn)
#> Error in `map()`:
#> ! Computation failed in index 4
#> Caused by error in `.f()` at purrr/R/map.R:117:2:
#> ! oh no

map(-1:3, \(x) fn(x))
#> Error in `map()`:
#> ! Computation failed in index 4
#> Caused by error in `fn()`:
#> ! oh no

Created on 2022-09-21 with reprex v2.0.2

@hadley hadley marked this pull request as ready for review September 21, 2022 22:22
Copy link
Member

@lionel- lionel- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! But I think it'd make sense to think about call arguments to take ownership of map errors, as argued inline.

R/map.R Outdated Show resolved Hide resolved
R/map.R Outdated Show resolved Hide resolved
R/map.R Outdated Show resolved Hide resolved
src/conditions.c Show resolved Hide resolved
tests/testthat/_snaps/keep.md Outdated Show resolved Hide resolved
@@ -3,7 +3,13 @@
Code
map_depth(x1, 6, length)
Condition
Error in `map_depth()`:
Error in `.fmap()`:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably fine for this particular function. I'm expecting this sort of errors to be internal errors rather than user errors.

tests/testthat/_snaps/map-if-at.md Outdated Show resolved Hide resolved
tests/testthat/_snaps/map2.md Outdated Show resolved Hide resolved
tests/testthat/_snaps/map2.md Outdated Show resolved Hide resolved
Extracting repeated code into map_, map2_, and pmap_
hadley added a commit that referenced this pull request Sep 22, 2022
R/map.R Show resolved Hide resolved
i = i,
error_call = .error_call,
.Call(map_impl, environment(), .type, .progress, .error_call)
)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is still pretty unfortunate that we get parent call information that mentions map_()

library(vctrs)
library(purrr)

map(1:5, vec_check_list)
#> Error in `map()`:
#> ! In index 1.
#> Caused by error in `map_()` at purrr/R/map.R:115:2:
#> ! `.x[[i]]` must be a list, not an integer.

It seems rare that the parent call is actually useful. I had mentioned that we might consider setting cnd$call <- NULL or hardcoding it to .f. Is that still an option? I feel like it would generally be less confusing because we'd have less chance of displaying confusing calls.

So it would look like:

> map(1:5, vec_check_list)
Error in `map()`:
! In index 1.
Caused by error:
! `.x[[i]]` must be a list, not an integer.
Run `rlang::last_trace()` to see where the error occurred.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I strip the call from the error, I definitely lose useful information in the tests. So I think in most cases it is helpful, although it doesn't work so well with error checking functions. And you can already fix that if needed:

library(purrr)

my_f <- function() {
  map(1:5, \(x) vctrs::vec_check_list(x, call = NULL))
}
my_f()
#> Error in `map()`:
#> ! Computation failed in index 1
#> Caused by error:
#> ! `x` must be a list, not an integer.

Created on 2022-09-23 with reprex v2.0.2

R/map.R Show resolved Hide resolved
src/coerce.c Show resolved Hide resolved
src/map.c Show resolved Hide resolved
src/map.c Show resolved Hide resolved
const char* l_name = CHAR(Rf_asChar(l_name_));
SEXP l = Rf_install(l_name);
SEXP pmap_impl(SEXP env, SEXP type_, SEXP progress, SEXP error_call) {
SEXP l = Rf_install(".l");
SEXP l_val = PROTECT(Rf_eval(l, env));
SEXPTYPE type = Rf_str2type(CHAR(Rf_asChar(type_)));

if (!Rf_isVectorList(l_val)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!Rf_isVectorList(l_val)) {
if (TYPEOF(l_val) != VECSXP) {

Rf_isVectorList() allows EXPRSXP for some reason and I feel like we don't want that. Does that feel right @lionel- ? https://github.com/wch/r-source/blob/79298c499218846d14500255efd622b5021c10ec/src/include/Rinlinedfuns.h#L851

src/map.c Show resolved Hide resolved
@hadley hadley merged commit 252bf6c into main Sep 23, 2022
@hadley hadley deleted the map-errors branch September 23, 2022 17:08
hadley added a commit that referenced this pull request Sep 27, 2022
allenbaron added a commit to DiseaseOntology/DO.utils that referenced this pull request Jan 6, 2023
Starting with purrr v1.0.0 the iteration when an error occurs is
shown (tidyverse/purrr#945). This was causing this test to fail
unnecessarily (it really shouldn't have been based on a snapshot).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Report map index in error
3 participants