Search code examples
rassertthat

assertthat return the message from the current function when called in a nested manner


Let me explain with the help of an example.

I have two functions :

fun1 <- function(x) {
  assertthat::assert_that(is.numeric(x), msg = 'Not a number')
  x
}

fun2 <- function(x) {
  assertthat::assert_that(x > 10, msg = 'Number not greater than 10')
  x + 10
}

They are called one into another.

fun1(x = fun2(20))
#[1] 30

However, if fun2 fails, I get message from fun1.

fun1(x = fun2(2))

Error: Not a number

I would expect to get message from fun2 itself which is 'Number not greater than 10'.

How can I get that?

I know I can break down the function calls like below which will resolve my issue.

y <- fun2(20)
fun1(x = y)

but this is a simplified example. In my real case, it is not possible to do this nor do I want to do it in that way.

Any ideas?


Solution

  • My understanding of the problem is that, assert_that() passes the ellipsis ... to see_if() and in this function the ellipsis is captured with eval(substitute(alist(...)). This prevents the error in fun2 to be evaluated. The error object is returned from fun2() and then checked against the condition in fun1(), checking whether the error object is.numeric which it isn't so the result is as expected 'Not a number'.

    One way to avoid this is to evaluate the result of fun2(). In your post you have shown one way, by using an intermediate object. Another way is to evalute the result of fun2() early using eval(bquote()). Below we use .() inside bquote() to evaluate fun2() early before the error object that is return is captured by see_if(). But I guess this is not your desired solution.

    fun1 <- function(x) {
      eval(bquote(assertthat::assert_that(.(is.numeric(x)), msg = 'Not a number')))
      x
    }
    
    fun2 <- function(x) {
      assertthat::assert_that(x > 10, msg = 'Number not greater than 10')
      x + 10
    }
    
    fun1(x = fun2(20))
    #> [1] 30
    
    fun1(x = fun2(5))
    #> Error: Number not greater than 10
    

    Since assert_that is just a drop-in replacement for stopifnot() the easier approach is to just use the latter - the error messages are not that bad:

    fun1 <- function(x) {
      stopifnot("Not a number" = is.numeric(x))
      x
    }
    
    fun2 <- function(x) {
      stopifnot('Number not greater than 10' = x > 10)
      x + 10
    }
    
    fun1(x = fun2(20))
    #> [1] 30
    
    fun1(x = fun2(5))
    #> Error in fun2(5): Number not greater than 10
    

    Created on 2023-02-27 by the reprex package (v2.0.1)