R/pipe.R

Defines functions as_pipe_fn new_lambda `%$%` `%T>%` `%<>%` pipe_nested pipe_lazy_masking pipe_eager_lexical `%>%`

Documented in pipe_eager_lexical pipe_lazy_masking pipe_nested

#' Pipe
#' 
#' Pipe an object forward into a function or call expression.
#' 
#' @param lhs A value or the magrittr placeholder.
#' @param rhs A function call using the magrittr semantics.
#' @details
#' \subsection{Using `%>%` with unary function calls}{
#' 
#' When functions require only one argument, `x %>% f` is equivalent
#' to `f(x)` (not exactly equivalent; see technical note below.)
#' 
#' }
#' \subsection{Placing `lhs` as the first argument in `rhs` call}{
#' 
#' The default behavior of `%>%` when multiple arguments are required
#' in the `rhs` call, is to place `lhs` as the first argument, i.e. 
#' `x %>% f(y)` is equivalent to `f(x, y)`.
#' }
#' \subsection{Placing `lhs` elsewhere in `rhs` call}{
#' 
#' Often you will want `lhs` to the `rhs` call at another position than the first.
#' For this purpose you can use the dot (`.`) as placeholder. For example,
#' `y %>% f(x, .)` is equivalent to `f(x, y)` and
#' `z %>% f(x, y, arg = .)` is equivalent to `f(x, y, arg = z)`.
#' }
#' 
#' \subsection{Using the dot for secondary purposes}{
#' 
#' Often, some attribute or property of `lhs` is desired in the `rhs` call in
#' addition to the value of `lhs` itself, e.g. the number of rows or columns.
#' It is perfectly valid to use the dot placeholder several times in the `rhs`
#' call, but by design the behavior is slightly different when using it inside
#' nested function calls. In particular, if the placeholder is only used 
#' in a nested function call, `lhs` will also be placed as the first argument!
#' The reason for this is that in most use-cases this produces the most readable 
#' code. For example, `iris %>% subset(1:nrow(.) %% 2 == 0)` is 
#' equivalent to `iris %>% subset(., 1:nrow(.) %% 2 == 0)` but
#' slightly more compact. It is possible to overrule this behavior by enclosing
#' the `rhs` in braces. For example, `1:10 %>% {c(min(.), max(.))}` is
#' equivalent to `c(min(1:10), max(1:10))`.
#' }
#' 
#' \subsection{Using `%>%` with call- or function-producing `rhs`}{
#' 
#' It is possible to force evaluation of `rhs` before the piping of `lhs` takes 
#' place. This is useful when `rhs` produces the relevant call or function.
#' To evaluate `rhs` first, enclose it in parentheses, i.e. 
#' `a %>% (function(x) x^2)`, and `1:10 %>% (call("sum"))`.
#' Another example where this is relevant is for reference class methods
#' which are accessed using the `$` operator, where one would do
#' `x %>% (rc$f)`, and not `x %>% rc$f`.
#' }
#' 
#' \subsection{Using lambda expressions with `%>%`}{
#' 
#' Each `rhs` is essentially a one-expression body of a unary function.
#' Therefore defining lambdas in magrittr is very natural, and as 
#' the definitions of regular functions: if more than a single expression
#' is needed one encloses the body in a pair of braces, \code{\{ rhs \}}.
#' However, note that within braces there are no "first-argument rule":
#' it will be exactly like writing a unary function where the argument name is
#' "`.`" (the dot).
#' 
#' }
#' \subsection{Using the dot-place holder as `lhs`}{
#' 
#' When the dot is used as `lhs`, the result will be a functional sequence, 
#' i.e. a function which applies the entire chain of right-hand sides in turn 
#' to its input. See the examples.
#' }
#' 
#' @section Technical notes:
#' The magrittr pipe operators use non-standard evaluation. They capture
#' their inputs and examines them to figure out how to proceed. First a function
#' is produced from all of the individual right-hand side expressions, and 
#' then the result is obtained by applying this function to the left-hand side.
#' For most purposes, one can disregard the subtle aspects of magrittr's 
#' evaluation, but some functions may capture their calling environment, 
#' and thus using the operators will not be exactly equivalent to the 
#' "standard call" without pipe-operators.
#' 
#' 
#' Another note is that special attention is advised when using non-magrittr
#' operators in a pipe-chain (`+, -, $,` etc.), as operator precedence will impact how the 
#' chain is evaluated. In general it is advised to use the aliases provided 
#' by magrittr.
#' 
#' @seealso \code{\link{\%<>\%}}, \code{\link{\%T>\%}}, \code{\link{\%$\%}}
#' 
#' @examples
#' # Basic use:
#' iris %>% head
#' 
#' # Use with lhs as first argument
#' iris %>% head(10)
#' 
#' # Using the dot place-holder
#' "Ceci n'est pas une pipe" %>% gsub("une", "un", .)
#'   
#' # When dot is nested, lhs is still placed first:
#' sample(1:10) %>% paste0(LETTERS[.])
#' 
#' # This can be avoided:
#' rnorm(100) %>% {c(min(.), mean(.), max(.))} %>% floor
#' 
#' # Lambda expressions: 
#' iris %>%
#' {
#'   size <- sample(1:10, size = 1)
#'   rbind(head(., size), tail(., size))
#' }
#' 
#' # renaming in lambdas:
#' iris %>%
#' {
#'   my_data <- .
#'   size <- sample(1:10, size = 1)
#'   rbind(head(my_data, size), tail(my_data, size))
#' }
#' 
#' # Building unary functions with %>%
#' trig_fest <- . %>% tan %>% cos %>% sin
#' 
#' 1:10 %>% trig_fest
#' trig_fest(1:10)
#' 
#' @rdname pipe
#' @export
`%>%` <- function(lhs, rhs) {
  lhs <- substitute(lhs)
  rhs <- substitute(rhs)
  kind <- 1L
  env <- parent.frame()
  lazy <- TRUE
  .External2(magrittr_pipe)
}

#' Lazy and eager pipes
#'
#' Assign these pipe variants to an infix symbol like `%>%`.
#'
#' @inheritParams %>%
#' @keywords internal
#' @export
pipe_eager_lexical <- function(lhs, rhs) {
  lhs <- substitute(lhs)
  rhs <- substitute(rhs)
  kind <- 1L
  env <- parent.frame()
  sym <- sys.call()[[1]]
  .External2(magrittr_pipe)
}
#' @rdname pipe_eager_lexical
#' @export
pipe_lazy_masking <- function(lhs, rhs) {
  lhs <- substitute(lhs)
  rhs <- substitute(rhs)
  kind <- 1L
  env <- parent.frame()
  lazy <- TRUE
  sym <- sys.call()[[1]]
  .External2(magrittr_pipe)
}
#' @rdname pipe_eager_lexical
#' @export
pipe_nested <- function(lhs, rhs) {
  lhs <- substitute(lhs)
  rhs <- substitute(rhs)
  kind <- 1L
  env <- parent.frame()
  nested <- TRUE
  sym <- sys.call()[[1]]
  .External2(magrittr_pipe)
}

#' Assignment pipe
#' 
#' Pipe an object forward into a function or call expression and update the 
#' `lhs` object with the resulting value.
#' 
#' @param lhs An object which serves both as the initial value and as target.
#' @param rhs a function call using the magrittr semantics.
#' 
#' @details The assignment pipe, \code{\%<>\%}, is used to
#' update a value by first piping it into one or more `rhs` expressions, and 
#' then assigning the result. For example, \code{some_object \%<>\% 
#' foo \%>\% bar} is equivalent to \code{some_object <- some_object \%>\% foo
#' \%>\% bar}. It must be the first pipe-operator in a chain, but otherwise it
#' works like \code{\link{\%>\%}}.
#' 
#' @seealso \code{\link{\%>\%}}, \code{\link{\%T>\%}}, \code{\link{\%$\%}}
#' 
#' @examples
#' iris$Sepal.Length %<>% sqrt
#' 
#' x <- rnorm(100)
#' 
#' x %<>% abs %>% sort
#' 
#' is_weekend <- function(day)
#' {
#'    # day could be e.g. character a valid representation
#'    day %<>% as.Date
#'    
#'    result <- day %>% format("%u") %>% as.numeric %>% is_greater_than(5)
#'    
#'    if (result)
#'      message(day %>% paste("is a weekend!"))
#'    else
#'      message(day %>% paste("is not a weekend!"))
#'    
#'    invisible(result)
#' }
#' 
#' @rdname compound
#' @export
`%<>%` <- function(lhs, rhs) {
  lhs <- substitute(lhs)
  rhs <- substitute(rhs)
  kind <- 2L
  lazy <- TRUE
  env <- parent.frame()
  .External2(magrittr_pipe)
}

#' Tee pipe
#' 
#' Pipe a value forward into a function- or call expression and return the
#' original value instead of the result. This is useful when an expression
#' is used for its side-effect, say plotting or printing.
#' 
#' @param lhs A value or the magrittr placeholder.
#' @param rhs A function call using the magrittr semantics.
#' 
#' @details The tee pipe works like \code{\link{\%>\%}}, except the 
#' return value is `lhs` itself, and not the result of `rhs` function/expression.
#' 
#' @seealso \code{\link{\%>\%}}, \code{\link{\%<>\%}}, \code{\link{\%$\%}}
#' 
#' @examples
#' rnorm(200) %>%
#' matrix(ncol = 2) %T>%
#' plot %>% # plot usually does not return anything. 
#' colSums
#' 
#' @rdname tee
#' @export
`%T>%` <- function(lhs, rhs) {
  lhs <- substitute(lhs)
  rhs <- substitute(rhs)
  kind <- 3L
  lazy <- TRUE
  env <- parent.frame()
  .External2(magrittr_pipe)
}

#' Exposition pipe
#' 
#' Expose the names in `lhs` to the `rhs` expression. This is useful when functions
#' do not have a built-in data argument.
#' 
#' @param lhs A list, environment, or a data.frame.
#' @param rhs An expression where the names in lhs is available.
#' 
#' @details Some functions, e.g. `lm` and `aggregate`, have a 
#' data argument, which allows the direct use of names inside the data as part 
#' of the call. This operator exposes the contents of the left-hand side object
#' to the expression on the right to give a similar benefit, see the examples.

#' @seealso \code{\link{\%>\%}}, \code{\link{\%<>\%}}, \code{\link{\%T>\%}}
#' 
#' @examples
#' iris %>%
#'   subset(Sepal.Length > mean(Sepal.Length)) %$%
#'   cor(Sepal.Length, Sepal.Width)
#'   
#' data.frame(z = rnorm(100)) %$% 
#'   ts.plot(z)
#'   
#' @rdname exposition
#' @export
`%$%` <- function(lhs, rhs) {
  lhs <- substitute(lhs)
  rhs <- substitute(rhs)
  kind <- 4L
  lazy <- TRUE
  env <- parent.frame()
  .External2(magrittr_pipe)
}


new_lambda <- function(exprs, env) {
  `_function_list` <- lapply(exprs, as_pipe_fn, env)

  structure(
    function(value) freduce(value, `_function_list`),
    class = c("fseq", "function")
  )
}

lambda_fmls <- as.pairlist(alist(. = ))
as_pipe_fn <- function(expr, env) {
  eval(call("function", lambda_fmls, expr), env)
}

#' FAQ: What is the gender of the pipe?
#'
#' In Magritte's original quote "Ceci n'est pas une pipe," the word
#' "pipe" is feminine. However the magrittr package quotes it as
#' "Ceci n'est pas un pipe," with a masculine "pipe." This
#' lighthearted misappropriation is intentional. Whereas the
#' object represented in Magritte's painting (a pipe that you can
#' smoke) is feminine in the French language, a computer pipe
#' (which is an Anglicism in French) is masculine.
#'
#' @name faq-pipe-gender
NULL

#' Eager pipe
#'
#' @description
#' Whereas `%>%` is lazy and only evaluates the piped expressions when
#' needed, `%!>%` is eager and evaluates the piped input at each step.
#' This produces more intuitive behaviour when functions are called
#' for their side effects, such as displaying a message.
#'
#' Note that you can also solve this by making your function
#' strict. Call `force()` on the first argument in your function to
#' force sequential evaluation, even with the lazy `%>%` pipe.
#' See the examples section.
#'
#' @inheritParams %>%
#'
#' @examples
#' f <- function(x) {
#'   message("foo")
#'   x
#' }
#' g <- function(x) {
#'   message("bar")
#'   x
#' }
#' h <- function(x) {
#'   message("baz")
#'   invisible(x)
#' }
#'
#' # The following lazy pipe sequence is equivalent to `h(g(f()))`.
#' # Given R's lazy evaluation behaviour,`f()` and `g()` are lazily
#' # evaluated when `h()` is already running. This causes the messages
#' # to appear in reverse order:
#' NULL %>% f() %>% g() %>% h()
#'
#' # Use the eager pipe to fix this:
#' NULL %!>% f() %!>% g() %!>% h()
#'
#' # Or fix this by calling `force()` on the function arguments
#' f <- function(x) {
#'   force(x)
#'   message("foo")
#'   x
#' }
#' g <- function(x) {
#'   force(x)
#'   message("bar")
#'   x
#' }
#' h <- function(x) {
#'   force(x)
#'   message("baz")
#'   invisible(x)
#' }
#'
#' # With strict functions, the arguments are evaluated sequentially
#' NULL %>% f() %>% g() %>% h()
#'
#' # Instead of forcing, you can also check the type of your functions.
#' # Type-checking also has the effect of making your function lazy.
#'
#' @name pipe-eager
#' @export
`%!>%` <- pipe_eager_lexical
smbache/magrittr documentation built on March 18, 2023, 6:21 p.m.