purrr icon indicating copy to clipboard operation
purrr copied to clipboard

Support functions in `otherwise` parameter of `possibly()`

Open deann88 opened this issue 6 years ago • 7 comments

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"

deann88 avatar Feb 14 '19 14:02 deann88

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.

lionel- avatar Feb 27 '19 10:02 lionel-

Also this might run into trouble when the error happens during input evaluation.

lionel- avatar Feb 27 '19 10:02 lionel-

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)
        })
    }
}

deann88 avatar Feb 27 '19 11:02 deann88

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?

lionel- avatar Feb 27 '19 11:02 lionel-

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)
        })
    }
}

deann88 avatar Feb 27 '19 12:02 deann88

execute could be a T/F value not a function to be called as is in the example. If TRUE execute otherwise.

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.

lionel- avatar Feb 27 '19 12:02 lionel-

Have you come up with a solution to this issue?

jjesusfilho avatar May 07 '20 21:05 jjesusfilho

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.

hadley avatar Aug 25 '22 20:08 hadley