wrapr_applicable

John Mount, Win-Vector LLC

2023-08-19

wrapr includes de-referencing, function evaluation, and a new concept called "wrapr_applicable". "wrapr_applicable" is dispatch by type of right hand side argument scheme.

Basic wrapr

The wrapr pipe operators (%.>% and %>.%) are roughly defined as: a %>.% b ~ { . <- a; b };. This works under the assumption that b is an expression with free-instances of “.”. A typical use is:

library("wrapr")

5 %.>% sin(.)
#> [1] -0.9589243

The above is performed by standard S3 dispatch on the left argument of an exported generic functions called apply_left() and apply_right(). A formal description of wrapr piping can be found here.

Dereferencing and function evaluation

wrapr works primarily over expressions and “.”. wrapr does tries to de-reference names found in the right-hand side of pipe stages, and also dispatches functions. One can also write the following.

5 %.>% sin
#> [1] -0.9589243
5 %.>% base::sin 
#> [1] -0.9589243

"wrapr_applicable"

Arbitrary objects ask wrapr to treat them as special expressions by overriding one or more of apply_left() and apply_right() for the S3 class they wish managed.

For example:

function_reference <- list(f = sin)
class(function_reference) <- c("wrapr_applicable", "ourclass")

apply_right.ourclass <- function(pipe_left_arg,
                                 pipe_right_arg,
                                 pipe_environment,
                                 left_arg_name,
                                 pipe_string,
                                 right_arg_name) {
  pipe_right_arg$f(pipe_left_arg)
}

function_reference
#> $f
#> function (x)  .Primitive("sin")
#> 
#> attr(,"class")
#> [1] "wrapr_applicable" "ourclass"

5 %.>% function_reference
#> [1] -0.9589243

function_reference$f <- sqrt
5 %.>% function_reference
#> [1] 2.236068

The signature arguments work as follows:

This functionality allows arbitrary objects to directly specify their intended pipeline behavior.

Let’s use a debugging function to see the values of all of the arguments.

apply_right.ourclass <- function(pipe_left_arg,
                                 pipe_right_arg,
                                 pipe_environment,
                                 left_arg_name,
                                 pipe_string,
                                 right_arg_name) {
  print("pipe_left_arg")
  print(pipe_left_arg)
  print("pipe_right_arg")
  print(pipe_right_arg)
  print("pipe_environment")
  print(pipe_environment)
  print("left_arg_name")
  print(left_arg_name)
  print("pipe_string")
  print(pipe_string)
  print("right_arg_name")
  print(right_arg_name)
  pipe_right_arg$f(pipe_left_arg)
}

5 %.>% function_reference
#> [1] "pipe_left_arg"
#> [1] 5
#> [1] "pipe_right_arg"
#> $f
#> function (x)  .Primitive("sqrt")
#> 
#> attr(,"class")
#> [1] "wrapr_applicable" "ourclass"        
#> [1] "pipe_environment"
#> <environment: R_GlobalEnv>
#> [1] "left_arg_name"
#> NULL
#> [1] "pipe_string"
#> [1] "%.>%"
#> [1] "right_arg_name"
#> function_reference
#> [1] 2.236068

a <- 5

a %.>% function_reference
#> [1] "pipe_left_arg"
#> [1] 5
#> [1] "pipe_right_arg"
#> $f
#> function (x)  .Primitive("sqrt")
#> 
#> attr(,"class")
#> [1] "wrapr_applicable" "ourclass"        
#> [1] "pipe_environment"
#> <environment: R_GlobalEnv>
#> [1] "left_arg_name"
#> a
#> [1] "pipe_string"
#> [1] "%.>%"
#> [1] "right_arg_name"
#> function_reference
#> [1] 2.236068

Conclusion

wrapr values (left-hand sides of pipe expressions) are completely general. wrapr operators (right-hand sides of pipe expressions) are primarily intended to be expressions that have “.” as a free-reference. wrapr can also be used with right-hand sides that are function references or with arbitrary annotated objects.