purrr
purrr copied to clipboard
Support functions in `otherwise` parameter of `possibly()`
I tried using possibly with the identity function and realized it failed because it expected a value. I propose modifying it in the following lines so that it could work with functions as otherwise values as well:
MyPossibly <- function(.f, otherwise, quiet = TRUE) {
.f <- as_mapper(.f)
force(otherwise)
function(...) {
tryCatch(.f(...), error = function(e) {
if (!quiet)
message("Error: ", e$message)
if (is.function(otherwise)) {
otherwise(...)
} else {
otherwise
}
}, interrupt = function(e) {
stop("Terminated by user", call. = FALSE)
})
}
}
> MyDate <- MyPossibly(as.Date, otherwise = identity)
> MyDate(";")
[1] ";"
> MyDate(" 2017-10-31")
[1] "2017-10-31"
I like it, but that's a breaking change. With this possibly()
can no longer be used with functions that return functions in the same way as before. Also for consistency we'd have to support formula lambdas, which would extend the breaking change to functions that return formulas.
Also this might run into trouble when the error happens during input evaluation.
Ok, So that it is not a breaking change a new parameter could be added.
MyPossibly <- function(.f, otherwise, quiet = TRUE, execute = FALSE) {
.f <- as_mapper(.f)
force(otherwise)
function(...) {
tryCatch(.f(...), error = function(e) {
if (!quiet)
message("Error: ", e$message)
if (execute) {
otherwise(...)
} else {
otherwise
}
}, interrupt = function(e) {
stop("Terminated by user", call. = FALSE)
})
}
}
Yes but we need to think about whether that UI makes sense and is not confusing for users, is easily documentable, what to do when both otherwise
and execute
are supplied, consider whether the added code complexity is worth this feature, ...
Also, what happens when the error occurs during input evaluation? Does this mean we need to first evaluate, then call .f
? If there was an error, should we go for otherwise
if supplied instead of execute
?
I agree. I am just saying that a functionality like this is needed. Not saying that this is the optimal implementation.
Regarding your questions:
execute
could be a T/F value not a function to be called as is in the example. If TRUE execute otherwise
.
Probably it is also a good idea to include a parameter which works as a fallback when the function call fails and should be provided whenever execute
is TRUE. Something like this:
MyPossibly <- function(.f, otherwise, quiet = TRUE, execute = FALSE, fallback = NULL) {
.f <- as_mapper(.f)
force(otherwise)
if (execute) {
if (is.null(fallback)) stop("please provide a fallback in case your function fails")
}
function(...) {
tryCatch(.f(...), error = function(e) {
if (!quiet)
message("Error: ", e$message)
if (execute) {
tryCatch(otherwise(...), error = function(e) {fallback })
} else {
otherwise
}
}, interrupt = function(e) {
stop("Terminated by user", call. = FALSE)
})
}
}
execute
could be a T/F value not a function to be called as is in the example. If TRUE executeotherwise
.
This would make it redundant with otherwise
.
Probably it is also a good idea to include a parameter which works as a fallback when the function call fails and should be provided whenever
execute
is TRUE
This is now erring towards a very confusing interface.
Have you come up with a solution to this issue?
I think this would have been a great idea if it had occurred to us when we first created possibly()
. But retrofitting it onto possible()
now it seems like it will be a lot of work, more work than is justified given the relatively mild improvement to the function.