Travis-CI Build StatusCRAN Status Badge

Introduction

frite is a package that enable anyone to easily write and modify functions with code. The tools it provides have applications in code generation or metaprogramming. It can also be used to inject, assign, or remove code in a function.

If you want to view the code, here is the GitHub repository.

The functions

  • Diagnostic
    • tictocify returns a nearly identical timed version of its input
    • is.output.same performs call on multiple functions and sees if they match
  • Modification
    • line_assign inserts assign() into a function
    • line_insert inserts code into a function
    • line_remove removes code from a function
    • copy_args copies the arguments of one function to another
  • Helping
    • list_body converts function body to a list
    • plot_body plots list_body() so you can inspect the body

Diagnostic functions

# Defining a new function
reduce_timed <- tictocify(reduce)

# Now to test it against the original reduce
reduce_timed(1:100000, sum, .init = 0) 
#> 0.21 sec elapsed
#> [1] 5000050000

is.output.same(reduce_timed(1:100000, sum, .init = 0), reduce)
#> 0.16 sec elapsed
#> [1] TRUE

tictocify will return a nearly identical timed version of a function. It does this by creating a function call and inserting tic(), toc(), and a return statement around the call.

is.output.same evaluates the call in the first argument and replaces the function in the first call and re-evaluates it with a new output and checks that they are identical.

# Constructing a different version of reduce_timed
(reduce_timed1 <- line_insert(reduce, after_line = 1, quote(tic())))
#> function (.x, .f, ..., .init) 
#> {
#>     tic()
#>     reduce_impl(.x, .f, ..., .init = .init, .left = TRUE)
#> }
#> <environment: namespace:purrr>

(reduce_timed1 <- line_assign(reduce_timed1, line = 3, 'value'))
#> function (.x, .f, ..., .init) 
#> {
#>     tic()
#>     assign("value", reduce_impl(.x, .f, ..., .init = .init, .left = TRUE))
#> }
#> <environment: namespace:purrr>

(reduce_timed1 <- line_insert(reduce_timed1, after_line = 3, quote(toc())))
#> function (.x, .f, ..., .init) 
#> {
#>     tic()
#>     assign("value", reduce_impl(.x, .f, ..., .init = .init, .left = TRUE))
#>     toc()
#> }
#> <environment: namespace:purrr>

(reduce_timed1 <- line_insert(reduce_timed1, after_line = 4,
                              quote(return(value))))
#> function (.x, .f, ..., .init) 
#> {
#>     tic()
#>     assign("value", reduce_impl(.x, .f, ..., .init = .init, .left = TRUE))
#>     toc()
#>     return(value)
#> }
#> <environment: namespace:purrr>

is.output.same(reduce_timed(1:100000, sum, .init = 0), reduce_timed1)
#> 0.18 sec elapsed
#> 0.16 sec elapsed
#> [1] TRUE

Note: Everything above can be piped.

These types of modifications can be difficult to make sequentially, so there are a couple of helper functions to allow you to see what you’re doing.

plot_body(map)
#> function (.x, .f, ...) 
#> {
#>     .f <- as_mapper(.f, ...)
#>     .Call(map_impl, environment(), ".x", ".f", "list")
#> }
#> <bytecode: 0x00000000139208e8>
#> <environment: namespace:purrr>
graphics::rect(xleft = -.15, ybottom = -.2, xright = 1.1, ytop = 1.2, xpd = TRUE)


map_hello <- map %>% 
  line_insert(after_line = 1, quote(print("Hello!")))

list_body(map_hello)
#> [[1]]
#> `{`
#> 
#> [[2]]
#> print("Hello!")
#> 
#> [[3]]
#> .f <- as_mapper(.f, ...)
#> 
#> [[4]]
#> .Call(map_impl, environment(), ".x", ".f", "list")

map_hello(list(1, 2, "b"), assertthat::is.number)
#> [1] "Hello!"
#> [[1]]
#> [1] TRUE
#> 
#> [[2]]
#> [1] TRUE
#> 
#> [[3]]
#> [1] FALSE

Recursively building functions

You can use the modification functions to build functions with recursion. A simple example is below.

spammer <- function() {}

add_print <- function(.f, n) {
  
  .f <- line_insert(.f, 1, quote(print("a")))
  
  n <- n - 1
  
  if (n > 0) return(add_print(.f, n))
  if (n == 0) return(.f)
}

add_print(spammer, 5)
#> function () 
#> {
#>     print("a")
#>     print("a")
#>     print("a")
#>     print("a")
#>     print("a")
#> }

If you want to learn more, you can view the reference manual or install frite and begin experimenting.

Installation

You can install frite from cran:

install.packages("frite")

Or you can install the development version from github:

install.packages("devtools")

devtools::install_github("visuelledata/frite")