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.

Community
  • 1
  • 1
Murta
  • 2,037
  • 3
  • 25
  • 33
  • 1
    You should look at the proto package. – IRTFM Jul 09 '15 at 00:27
  • 2
    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. – MrFlick Jul 09 '15 at 02:47
  • 1
    @MrFlick tks for your comment. I changed the post topic to make it clearer. – Murta Jul 11 '15 at 16:25
  • This topic is also discussed in `magrittr` issue #26: https://github.com/smbache/magrittr/issues/26 – ctbrown Aug 27 '15 at 15:45
  • About notation creation: [question 32305096](http://stackoverflow.com/questions/32305096) – Murta Sep 03 '15 at 12:16
  • @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. – ctbrown Sep 03 '15 at 17:08

3 Answers3

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 %?%.

Ben Bolker
  • 211,554
  • 25
  • 370
  • 453
  • 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. +1 – Murta Jul 11 '15 at 18:02
  • 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) ? – Murta Jul 11 '15 at 18:06
  • 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. – Ben Bolker Jul 11 '15 at 19:17
  • Any reason not to define the operator as `%<%`, to indicate that the data is sent from right to left? – Claus Wilke Jul 13 '15 at 07:34
  • @ClausWilke I think it's a nice suggestion. – Murta Jul 13 '15 at 12:54
  • @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. – ctbrown Sep 03 '15 at 16:44
8

How about using compose from hadley's purrr package?

compose(sum,abs,round,c)(1.5,-2.3,3.4)
Neal Fultz
  • 9,282
  • 1
  • 39
  • 60
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.

ctbrown
  • 2,271
  • 17
  • 24