rfunctiontidyversemagrittr

What the magrittr pipe %>% does in the shadows


Those two functions don't give the same output, because of how works the %>% operator, but I can't understand why?

my_function <- function(x) {
 # Extract the name of x
 the_name_of_x <- deparse(substitute(x))
 print(the_name_of_x)
}

my_other_function <- function(x) {
 # Extract the name of x
 the_name_of_x <- x %>% substitute() %>% deparse()
 print(the_name_of_x)
}

# Example
my_function("zaza")
# [1] "\"zaza\""
my_other_function("zaza")
# [1] "x"

I really can't figure out why this makes a difference.


Solution

  • You would get the same value as the first one if you used the native pipe

    yet_another_function <- function(x) {
      # Extract the name of x
      the_name_of_x <- x |> substitute() |> deparse()
      print(the_name_of_x)
    }
    

    The reason is the |> pipe actually re-writes the syntax tree so it's not actually a function. Observe

    quote(x |> substitute() |> deparse())
    deparse(substitute(x))
    

    However %>% is a function. It will appear on the call stack. The substitute function uses non-standard evaluation which means it looks at the expression passed to the function. The %>% function cannot re-write the call completely. It evaluates the promise of the function parameter into an expression. substitute is a special function in that if you want to capture the value a promise points to, you need to do so before the promise is evaluated.

    You can see the source of the magrittr pipe function

    `%>%`
    function (lhs, rhs) 
    {
        lhs <- substitute(lhs)
        rhs <- substitute(rhs)
        kind <- 1L
        env <- parent.frame()
        lazy <- TRUE
        .External2(magrittr_pipe)
    }
    

    And you can see how it's already using substitute to get the unevaluated expressions of it's parameters. A promise cannot survive this indirection.