Is right-to-left operator associativity in R possible?
Asked Answered
F

3

21

I'm new to R, and I just discovered I suffer from Bracket Phobia (see comment in the link). I like the way magrittr notation %>% works, because it avoids nested parenthesis in some situations, and makes code more readable. I came from Mathematica, where there is a very similar native // notation to do what %>% does. Here are some R and Mathematica comparisons:

#R Notation    
c(1.5,-2.3,3.4) %>% round %>% abs %>% sum  

#Mathematica Notation
{1.5,-2.3,3.4}//Round//Abs//Total

So far so good, but, my question is:

Is there some way to mimic Mathematica @ notation, with right-to-left associativity in R?

Here is how it works in Mathematica, to solve the same code above:

Total@Abs@Round@{1.5,-2.3,3.4}

In Mathematica it can also be write as:

Total[Abs[Round[{1.5,-2.3,3.4}]]]

just like in R it would be:

sum(abs(round(c(1.5,-2.3,3.4))))

But it would be much more clean (and cool) to have in R something like this:

sum@abs@round@c(1.5,-2.3,3.4)

PS: I know @ is used in S4 classes, and is not a good idea. This is just an illustrative comparison.

Fraught answered 8/7, 2015 at 23:4 Comment(6)
You should look at the proto package.Belongings
Do do something like this in R you'd need an operator with right-to-left associativity and it doesn't appear to be possible to define such an operator.Pipsissewa
@Pipsissewa tks for your comment. I changed the post topic to make it clearer.Fraught
This topic is also discussed in magrittr issue #26: github.com/smbache/magrittr/issues/26Cholecystotomy
About notation creation: question 32305096Fraught
@murta thanks for the links on the github issue. Please also see the my response to your question. I think it is a better solution with fewer limitations. You might want to pick it as the correct solution.Cholecystotomy
C
7

The backpipe package was designed and created for this purpose. It provides a backpipe (right-to-left) operator for magrittr, pipeR and generally for any forward pipe operator. backpipe can be found on github and on CRAN.

library(magrittr)  # or library(pipeR)
library(backpipe)

x <- c(1.5,-2.3,3.4)
sum %<% abs %<% round %<% x

all.equal(                             # TRUE
      x %>% round %>% abs %>% sum,
      sum %<% abs %<% round %<% x
)

backpipe also is not limited by additional parameters as is the case of the @BenBolker's solution. This, for example, works with backpipe :

mean(na.rm=TRUE) %<% c(1:3,NA)  

See also the discussion on the magrittr github issue that discuss this.

Cholecystotomy answered 3/9, 2015 at 17:4 Comment(0)
R
16

I haven't tested/thought about this carefully at all, but defining function composition via an operator (as below) seems to work in a couple of test cases:

library(magrittr)

## operator defined as  "left-pointing arrow" at the 
##    suggestion of @ClausWilke:
"%<%" <- function(x,y) { if (is(y,"function")) 
          function(z) x(y(z)) 
      else x(y) }

x <- c(1.5,-2.3,3.4)
all.equal(x %>% round %>% abs %>% sum,
          sum %<% abs %<% round %<% x)

x <- rnorm(1000)
all.equal(x %>% round %>% abs %>% sum,
          sum %<% abs %<% round %<% x)

The syntax is not as nice as being able to use a single character (such as @) for the composition operator, but even if you could find an unused one (all the obvious ones [!@#$%^&*~|] are taken, and masking them would be a terrible idea) there's a parser limitation: user-defined binary operators in R must be of the form %?%.

Rosierosily answered 11/7, 2015 at 17:42 Comment(6)
This is very interesting. It's a pity that wrap with % is needed, but I believe that is the best that can be done in R by users. I'll wait until the end of the bounty to choose your answer, just to stimulate another ones. +1Fraught
Just for curiosity. Do you know if it's possible to change parser behaviour? Like, to test something like: sum>>abs>>round>>c(1.5,-2.3,3.4) ?Fraught
Not without extreme low-level hacking of the R source code ... e.g. you'd have to find the parsing code in the low-level C code and modify it appropriately.Rosierosily
Any reason not to define the operator as %<%, to indicate that the data is sent from right to left?Haematoxylon
@ClausWilke I think it's a nice suggestion.Fraught
@BenBolker I like the simplicity, but it doesn't quite work. Consider: ` mean(na.rm=TRUE) %<% c(1:3,NA). The additional argument to mean` breaks your solution.Cholecystotomy
A
8

How about using compose from hadley's purrr package?

compose(sum,abs,round,c)(1.5,-2.3,3.4)
Acclimatize answered 11/7, 2015 at 18:21 Comment(0)
C
7

The backpipe package was designed and created for this purpose. It provides a backpipe (right-to-left) operator for magrittr, pipeR and generally for any forward pipe operator. backpipe can be found on github and on CRAN.

library(magrittr)  # or library(pipeR)
library(backpipe)

x <- c(1.5,-2.3,3.4)
sum %<% abs %<% round %<% x

all.equal(                             # TRUE
      x %>% round %>% abs %>% sum,
      sum %<% abs %<% round %<% x
)

backpipe also is not limited by additional parameters as is the case of the @BenBolker's solution. This, for example, works with backpipe :

mean(na.rm=TRUE) %<% c(1:3,NA)  

See also the discussion on the magrittr github issue that discuss this.

Cholecystotomy answered 3/9, 2015 at 17:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.