Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extending partial() to comprehend quasiquotation #457

Closed
egnha opened this issue Feb 6, 2018 · 1 comment

Comments

@egnha
Copy link
Contributor

commented Feb 6, 2018

It would be nice if partial() were augmented to comprehend quasiquotation, in a way that:

  • preserves the API—maintains the current behavior and argument signature
  • enables partialized arguments to be unquoted and spliced

(Additionally, in the degenerate case where no partialized arguments are supplied, partial() should simply apply as_mapper().)

One possibility is as follows:

library(nofrills)

partial <- function(...f, ..., .env = parent.frame(), .lazy = TRUE, .first = TRUE) {
  ...f <- as_mapper(...f)
  args <- if (.lazy) exprs(...) else lapply(quos(...), eval_tidy)
  
  if (is_empty(args))
    return(...f)
  
  env <- child_env(.env, ...f = ...f)
  if (.first)
    nofrills::fn(... ~ ...f(!!! args, ...), ..env = env)
  else
    nofrills::fn(... ~ ...f(..., !!! args), ..env = env)
}

Remarks:

  • nofrills::fn() is a simple quasiquotation-enabled function constructor. Though its use is not strictly necessary, it facilitates a very succinct definition of partial().

  • This version of partial() is compatible with byte-compiled functions (a difficulty I'd overlooked in #349).

  • Issues #360, #387 are addressed.

Some examples showing quasiquotation and compatibility with the current behavior:

# Partialized arguments are lazily evaluated, by default
f <- partial(runif, n = rpois(1, 5))
f
#> function (...) 
#> ...f(n = rpois(1, 5), ...)
#> <environment: 0x10c5338d8>
f() #> [1] 0.5433265
f() #> [1] 0.9367817 0.7762598

# Eager evaluation by setting .lazy = FALSE
f <- partial(runif, n = rpois(1, 5), .lazy = FALSE)
f
#> function (...) 
#> ...f(n = 3L, ...)
#> <environment: 0x10c594ae0>
f() #> [1] 0.9508948 0.8669053 0.4992270
f() #> [1] 0.04073824 0.93821289 0.43544412

# Eager evaluation by unquoting
partial(runif, n = !! rpois(1, 5))
#> function (...) 
#> ...f(n = 4L, ...)
#> <environment: 0x10c5b5aa8>

# Arguments can be spliced eagerly
partial(runif, !!! list(n = rpois(1, 5), max = 2))
#> function (...) 
#> ...f(n = 3L, max = 2, ...)
#> <environment: 0x10c64ef48>
  
# Arguments can be spliced lazily
partial(runif, !!! quos(n = rpois(1, 5), max = 2))
#> function (...) 
#> ...f(n = rpois(1, 5), max = 2, ...)
#> <environment: 0x10c670e30>

(For now, I'm omitting a better print method to focus on the main point.)

@egnha

This comment has been minimized.

Copy link
Contributor Author

commented Feb 7, 2018

Erratum: the approach in #349 is viable after all. (Explanation there.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.