Not possible to use special ( ` ) functions with base pipe in R

Viewed 267

With the magrittr pipe, one could use backticks ( ` ) to include special functions1 in the pipe chain. For example:

library(magrittr)
c(7, 6, 5) %>% sort() %>% `[`(1)
#> [1] 5

# and

3 %>% `-`(4)
#> [1] -1

are the same as

v <- c(7, 6, 5)
v <- sort(v)
v[1]
#> [1] 5

# and

3 - 4 
#> [1] -1

However, the native R pipe |> does not (yet?) allow to do that. For example:

c(7, 6, 5) |> sort() |> `[`(1)
#> Error: function '[' not supported in RHS call of a pipe

# and

3 |> `-`(4)
#> Error: function '-' not supported in RHS call of a pipe

Is there a reason why this has not been technically implemented (in case, what is the reason and are there plans to change this?) and are there any workarounds that are not too tortuous?


1 I don't know how to correctly refer to those functions (behind operators such as + or []) that need backticks to be called in their standard function form. Please edit where I say "special functions" if a more appropriate expression exists.

4 Answers

The help file for the native pipe operator |> states:

To avoid ambiguities, functions in rhs calls may not be syntactically special, such as + or if.

So it is not that the [ function is not implemented in the pipe, but rather that the [ symbol has been specifically prevented from being used in the pipe to prevent parsing ambiguities. There is certainly a case for arguing that the syntax '['(op1, op2) or '-'(op1, op2) could be confusing for new users, and results in code that is less clean and idiomatic. There may be more to it than that - perhaps some edge cases where there would be genuine parsing ambiguity, but I can't think of any, as long as backticks are used around special symbols.

Anyway, this means you can define a function as an alias for [ (a bit like magrittr's extract), for example:

obtain <- `[`

c(7, 6, 5) |> sort() |> obtain(1)
#> [1] 5

takeaway <- `-`

c(7, 6, 5) |> sort() |> takeaway(1)
#> [1] 4 5 6

You may use an anonymous function

c(7, 6, 5) |> sort() |> {function(x) x[1]}()
#[1] 5

3 |> {function(x) x - 4}()
#[1] -1

Some workarounds are to surround [ with parentheses, use (\(x) x[1])() notation, use do.call or find a function another function to perform the required operation. The first 4 below are general workarounds. the next two use the Bizarro pipe and magrittr pipe whereas the remainder depend on the particular operation.

The last three examples below are less general than the others but show that sometimes using a different approach can work.

c(7, 6, 5) |> sort() |> (`[`)(1)
## [1] 5

c(7, 6, 5) |> sort() |> (\(x) x[1])()
## [1] 5

c(7, 6, 5) |> sort() |> list(1) |> do.call(what = `[`)
## [1] 5

`%[%` <- `[`
c(7, 6, 5) |> sort() |> `%[%`(1)
## [1] 5

c(7, 6, 5) |> sort() ->.; .[1]  # Bizarro pipe
## [1] 5

library(magrittr)
c(7, 6, 5) %>% sort() %>% .[1]
## [1] 5

c(7, 6, 5) |> sort() |> head(1) 
## [1] 5

c(7, 6, 5) |> sort() |> .subset(1)
## [1] 5

3 |> c(-4) |> sum()
## [1] -1

To add another possible workaround, you can use .Primitive like this:

3 |> .Primitive("+")(4)
[1] 7

c("a","b") |> .Primitive("[")(1)
[1] "a"
Related