Skip to content

Freezing calls, modifying arguments and evaluating later

License

Notifications You must be signed in to change notification settings

coolbutuseless/cryogenic

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cryogenic

Lifecycle: experimental R build status

cryogenic is a package for freezing a function call, modifying its arguments and then evaluating later.

This package is written for a particular use case I have in mind, and is not trying to be the comprehensive answer to call manipulation in R.

My Use Case

  • Both the calling environment and evaluation environment are under my control.
    • This package is not targetted at executing captured calls based on possibly hostile user input.
  • Need an R package for this as I need to use this code across multiple packages
  • Need eager evaluation of arguments when call is captured
  • Not concerned with lazy evaluation of the call arguments.
  • Need to be able to update any arguments
    • Need to be able to update an argument to have a value of NULL
  • Need to be able to set arguments to a default value if they aren’t already present
  • Nearly everything is in lists
  • Need to be able to consistently add arbitrary meta information as an attribute on the call

What’s in the box

  • capture_call() to capture a call without evaluating it
  • modify_call() to modify call arguments
  • evaluate_call() to evaluate a call - and optionally modify the call arguments just prior to the evaluation.

Installation

You can install from GitHub with:

# install.package('remotes')
remotes::install_github('coolbutuseless/cryogenic')

Simple example 1

  • capture a call
  • modify the call
  • evaluate the call
library(cryogenic)

cc <- capture_call(runif(n = 10, min=-3))
cc
#> runif(n = 10, min = -3)
cc <- modify_call(cc, defaults = list(min=0, max=5), update = list(n = 5))
cc
#> runif(n = 5, min = -3, max = 5)
evaluate_call(cc)
#> [1] -0.8759307 -0.0230088  1.5828269  4.2656623 -1.3865446

Simple example 2

  • capture a call
  • simultaneously update a call and evalute it
library(cryogenic)

cc <- capture_call(runif(n = 10, min=-3))
cc
#> runif(n = 10, min = -3)
evaluate_call(cc, defaults = list(min=0, max=5), update = list(n = 15))
#>  [1]  4.18711748  4.55740215  2.28638234  2.03291235 -2.50570984 -1.35220340
#>  [7] -1.58754598  2.49618277  0.07282975  3.15873136  0.98159394  2.74094807
#> [13]  4.93524876  0.04028144  3.21956177

Simple example 3

library(cryogenic)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Capture a call as-is.
#
# * the function does not have to exist in the capturing environment
# * By default, all arguments are eagerly evaluated in the environment in which
#   the call was captured
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc <- capture_call(farnarkle(6, x = 2, y = 3, z = 2+9, error = TRUE))
cc
#> farnarkle(6, x = 2, y = 3, z = 11, error = TRUE)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Update the arguments to the call
#
# * 'defaults' are only added to the arguments if the named argument does 
#    not already exist there.
# * 'update' values are added to the arguments unconditionally
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc <- modify_call(
  cc, 
  defaults = list(x = NULL, y = 7), 
  update   = list(z = NULL, na.rm = TRUE),
  delete   = c('error')
)

cc
#> farnarkle(6, x = 2, y = 3, z = NULL, na.rm = TRUE)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 'farnarkle' is not yet defined, so evaluation should fail
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
evaluate_call(cc)
#> Error in farnarkle(6, x = 2, y = 3, z = NULL, na.rm = TRUE): could not find function "farnarkle"
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# define farnarkle and it should work
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
farnarkle <- function(...) {print("Hello #Rstats")}
evaluate_call(cc)
#> [1] "Hello #Rstats"

Related Software

  • rlang offers a lot of tools for call modification, but wasn’t quite a match for how I’m working.
  • pryr::modify_call() is similar to what is done in this package, but cryogenic offers a few more features I need.

Acknowledgements

  • R Core for developing and maintaining the language.
  • CRAN maintainers, for patiently shepherding packages onto CRAN and maintaining the repository

About

Freezing calls, modifying arguments and evaluating later

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages