Discussion on the use of validate(need()) vs if()validate()

This is a continuation of a discussion on Twitter (see https://twitter.com/hadleywickham/status/1275426462447792130?s=20 and previous tweets).

In brief, what is the preferred mode of running validation checks on input variables in Shiny?

An example below:

# need + validate -----------------------------------------------------------
validate(
  need(
    check_mzr_object(ms_object$mzr_connection),
    "Wasn't able to connect to MS file"
  )
)
validate(
  need(!is.null(input$ppm_input), "ppm_input must not be null"),
  need(input$ppm_input > 0L, "ppm_input must be > 0")
)


# if + validate -----------------------------------------------------------
if (!check_mzr_object(ms_object$mzr_connection)) {
  validate("Wasn't able to connect to MS file")
}

if (is.null(input$ppm_input)) {
  validate("ppm_input must not be null")
} else if (input$ppm_input <= 0L) {
  validate("ppm_input must be > 0")
}

its entirely a matter of personal taste / style.

1 Like

I agree with both points of view here. I would add that shiny-specific functions like validate+need or req are not as explicit about the condition they're testing. With a if statement, you have to be very explicit about nulls, empty, length, etc whereas the shiny functions are convenience shortcuts - they're more compact but also less precise in my opinion.

1 Like

Yeah, need is just a convenience function, but aren't most things?:

shiny::need
function (expr, message = paste(label, "must be provided"), label) 
{
    force(message)
    if (!isTruthy(expr)) 
        return(message)
    else return(invisible(NULL))
}
#--------------------------
shiny::isTruthy
function (x) 
{
    if (inherits(x, "try-error")) 
        return(FALSE)
    if (!is.atomic(x)) 
        return(TRUE)
    if (is.null(x)) 
        return(FALSE)
    if (length(x) == 0) 
        return(FALSE)
    if (all(is.na(x))) 
        return(FALSE)
    if (is.character(x) && !any(nzchar(stats::na.omit(x)))) 
        return(FALSE)
    if (inherits(x, "shinyActionButtonValue") && x == 0) 
        return(FALSE)
    if (is.logical(x) && !any(stats::na.omit(x))) 
        return(FALSE)
    return(TRUE)
}

To be clear, I think your approach is fine, and I'm not saying you shouldn't use it. But I currently don't plan to teach it because I think it's a distraction. (Also if you validation is that complex wouldn't you want it be in a function so you could test it outside of Shiny?)

— Hadley Wickham (@hadleywickham) June 23, 2020

Personal preference:

I usually separate things so that anything that fails in Shiny uses validate(need), so reactives, inputs, are tested with that so that custom CSS can be applied and, more importantly, helpful messages can be displayed not just "on error" but as a suggestive message of how to proceed through the app.

I don't think so, in almost every web interface registering a password you get instant feedbacks about the password you're setting. For example on GitHub: pic.twitter.com/kmCxcmxHDX

— Colin Fay 🤘 (@_ColinFay) June 23, 2020

If a function is vitally important, and its logic is not dependent on Shiny, I write as a function (like writing a package) that has zero dependency on shiny. In that case a regular if(FALSE)stop("message") is used, no validation(). An added benefit is now this function can be borrowed for use elsewhere without calling a dependency on Shiny.

Also, pulling out app functionality into functions that are independent of Shiny is a fantastic way to tell when a new, maybe small, package should be created that contains a subset of functions that the larger work uses.

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.