Skip to content

Commit

Permalink
strictly respects lazy evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
egnha committed Mar 7, 2017
1 parent 54904d4 commit bd902df
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 27 deletions.
29 changes: 17 additions & 12 deletions R/strictly.R
Original file line number Diff line number Diff line change
Expand Up @@ -282,22 +282,27 @@ print.strict_closure <- function(x, ...) {
#'
#' @return
#' \subsection{\code{strictly}}{
#' If neither the check formulae nor the switch \code{.warn_missing} are
#' applicable to \code{.f}, then \code{strictly} simply returns \code{.f},
#' unaltered; this is the case when \code{.f} has no named arguments, i.e.,
#' \code{strictly} does nothing if it has nothing to do: \code{.f} is
#' returned unaltered if \code{.f} has no named arguments to check (i.e.,
#' \code{.f} has argument signature \code{function()} or
#' \code{function(...)}. Otherwise, \code{strictly} returns a function that
#' behaves \emph{identically} to \code{.f}, with the exception that it
#' validates its inputs before being called on them. For every failed
#' validation, an error is signaled.
#' \code{function(...)}).
#'
#' \code{strictly} preserves the argument signature of \code{.f}, along with
#' its attributes (with the execption that the resulting class is
#' \code{"strict_closure"}, which inherits from the class of \code{.f}).
#' Otherwise, \code{strictly} returns a function that behaves
#' \emph{identically} to \code{.f}, with the exception that it validates its
#' inputs before being called on them. In particular, \code{strictly}
#' respects lazy evaluation: if all checks pass, then in the call to the
#' underlying function, all function arguments, irrespective of the checks,
#' are still lazily evaluated. An error is signaled if any check fails; this
#' error tabulates every failing check.
#'
#' Additionally, \code{strictly} preserves the argument signature of
#' \code{.f}, along with its attributes. (Sole exception: the resulting
#' class is \code{"strict_closure"}, which contains the class of \code{.f}.)
#' }
#'
#' \subsection{\code{nonstrictly}}{
#' The original function, stripped of any input validation checks imposed by
#' \subsection{\code{nonstrictly} (\code{freely})}{
#' \code{nonstrictly} (and its alias, \code{freely}) return the original
#' function, stripped of any input validation checks imposed by
#' \code{strictly}.
#' }
#'
Expand Down
35 changes: 20 additions & 15 deletions man/strictly.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions tests/testthat/test-strictly.R
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,22 @@ test_that(".warn_missing value does not change checks", {
expect_warning(purrr::safely(f1_warn_true)(y = 1),
"Missing required argument\\(s\\): x")
})

test_that("arguments, whether checked or not, are evaluated lazily", {
f <- function(x, y) if (x) "True" else y
msg <- "`y` not an error"
chk_is_error <-
list(msg ~ tryCatch({y; FALSE}, error = function(e) TRUE)) ~ isTRUE

f_strict <- strictly(f, chk_is_error)

# Verify that y is checked:
# - Pass
expect_error(f_strict(TRUE, stop("!")), NA)
# - Fail
not_error <- list(TRUE, FALSE, NULL, NA_real_, NaN, letters, mtcars)
for (. in not_error) expect_error(f_strict(TRUE, .), msg)

# Verify that y is not forced when function called
expect_identical(f_strict(TRUE, stop("!")), "True")
})

0 comments on commit bd902df

Please sign in to comment.