Search code examples
rdplyrpipeassignment-operatormagrittr

How to define the pipe operator?


I like the pipe (in or as mentioned in the comments) a lot but sometimes I run into errors which show me that I lack some understanding. In case of other functions or operators I mostly have at least some idea how they may be defined, but in case of the pipe I do not have even a clue where to start if I want to define it.

I am really curious how the pipe is defined. So far I simply tried to assign it to an own operator but even this does not work. See here:

library(dplyr)

`%pipe%` <- `%>%`

data.frame() %pipe% class(.)
# gives an error: subscript out of bounds

What magical ingredients make a pipe work? How could we define it ourselves? Thanks.


Solution

  • magrittr's code itself checks if the pipe you used is named as one of the few allowed names, as seen at the top here: https://github.com/tidyverse/magrittr/blob/master/R/is_something.R .

    In fact all pipes have the same code, copied from magrittr:::pipe as seen as the bottom of this script :

    https://github.com/tidyverse/magrittr/blob/master/R/pipe.R

    The behavior depends on the name of the pipe, it's really odd and I've never seen this anywhere else.

    I'll try to make it less confusing by inventing a fake function behaving likewise, forgive the silliness of the example.

    concat <- function(x,y) {
      # extract quoted function name from call
      fun <- sys.call()[[1]]
    
      # check if it's one of our possible names, and act accordingly
      if(identical(fun, quote(`%upconcat>%`))) {
        return(toupper(paste0(x,y)))
      } 
      if(identical(fun, quote(`%lowconcat>%`))) {
        return(tolower(paste0(x,y)))
      } 
      stop("Unsupported operator!")
    }
    `%upconcat>%` <- `%lowconcat>%` <- concat
    
    "XoX" %upconcat>% "oXo"
    #> [1] "XOXOXO"
    "XoX" %lowconcat>% "oXo"
    #> [1] "xoxoxo"
    
    `%foo>%` <- `%upconcat>%`
    "XoX" %foo>% "oXo"
    #> Error in "XoX" %foo>% "oXo": Unsupported operator!
    

    You see there's no way to copy the pipe unless you modify the is_pipe function itself, which you can't do...

    Just kidding, of course you can, but probably you shouldn't ?

    
    library(magrittr)
    is_pipe <- function(pipe) {
      identical(pipe, quote(`%>%`))   ||
        identical(pipe, quote(`%T>%`))  ||
        identical(pipe, quote(`%<>%`))  ||
        identical(pipe, quote(`%$%`)) ||
        identical(pipe, quote(`%pipe%`)) # <- added line
    }
    
    assignInNamespace("is_pipe", is_pipe, "magrittr")
    `%pipe%` <- `%>%`
    
    data.frame() %pipe% class(.)
    #> [1] "data.frame"
    

    Created on 2020-05-29 by the reprex package (v0.3.0)