I’m trying to build a wrapper around the base pipe |>
to do some modification to the left-hand side and then pipe it forward normally. While working on this I found the following peculiarity about the base pipe compared to other operators:
Work as expected:
`+`(1,2)
#> [1] 3
`<-`("foo", 1)
foo
#> [1] 1
library(magrittr)
`%>%`(1, print)
#> [1] 1
Does not work as expected:
`|>`(1, print)
#> Error in `|>`(1, print): could not find function "|>"
This means that, for example, while the following works fine with the magrittr
pipe
as.call(c(as.name("%>%"), "foo", substitute(print)))
#> "foo" %>% print
and can be passed to eval()
:
eval(as.call(c(as.name("%>%"), "foo", substitute(print))))
#> [1] "foo"
It does not work for the base pipe:
eval(as.call(c(as.name("|>"), "foo", substitute(print))))
#> Error in `|>`("foo", print): could not find function "|>"
I’ve struggled to find documentation on this because I’m not quite sure how to describe this problem, and google often doesn’t play nice with queries involving special characters, even in quotations.
Edit: more detail on the context
As a toy example, say we want to make a pipe variant that pipes the name of the object, rather than the object, and without otherwise changing our usage:
library(magrittr)
`%>>%` <- function(lhs, rhs) {
# get lhs as a character
lhs.name <- deparse(substitute(lhs))
# the parent environment
parent <- parent.frame()
# the environment in which to evaluate pipeline
env <- new.env(parent = parent)
# magrittr version
eval(
as.call(c(as.name("%>%"), lhs.name, substitute(rhs))),
env, env
)
}
foo_func <- function(arg1, arg2, arg3) {
print(arg1)
print(arg2)
print(arg3)
arg2 + arg3
}
thing %>>% foo_func(2,4)
#> [1] "thing"
#> [1] 2
#> [1] 4
#> [1] 6
To avoid the magrittr
dependency, I was interested in making this work with |>
instead of %>%
. It appears we can substitute in
eval(
str2lang(paste0("'", lhs.name, "'", " |> ", deparse(substitute(rhs)))),
env, env
)
though I have not yet tested it thoroughly.
8
1) If the question is how to implement “%>>%” without dependencies then copy insert_dot
source code from the poorman source code at
https://github.com/nathaneastwood/poorman/blob/master/R/pipe.R
and modify the source code of %>%
on the same page to be:
"%>>%" <- function(lhs, rhs) {
rhs_call <- insert_dot(substitute(rhs))
eval(rhs_call, envir = list(`.` = substitute(lhs)), enclos = parent.frame())
}
# tests
if (exists("xyz")) rm(xyz)
xyz %>>% paste0("*")
## [1] "xyz*"
xyz %>>% paste(., .)
## [1] "xyz xyz"
2) All we are really saving with this new pipe operator is one leg in a pipeline containing substitute
. May it is sufficient not to define a new piping operator at all and just write:
xyz |> substitute() |> paste0("*")
## [1] "xyz*"
xyz |> substitute() |> ( (x) paste(x, x) )()
## [1] "xyz xyz"
or use quote
xyz |> quote() |> paste0("*")
## [1] "xyz*"
xyz |> quote() |> ( (x) paste(x, x) )()
## [1] "xyz xyz"
3) If a dependency on magrittr is ok then
library(magrittr)
"%>>%" <- function(lhs, rhs) {
eval.parent(substitute(lhs %>% substitute %>% rhs))
}