Search code examples
rpipemagrittr

Piping into `if` returns the pipeline without evaluating it


I am trying to implement a pipeline that has an optional step which consists of a pipeline of several functions. It runs this pipeline based on a condition, and otherwise it just passes through the original value. However I've tried implementing this using if and also purrr::when, but in both cases the positive case simply returns the pipeline instead of executing it.

Here's a simple contrived example. I've simplified the conditional pipeline to only one function, but being able to use magrittr pipes inside the TRUE branch is important here.

library(magrittr)

maybe_round = function(x, round = TRUE){
  x %>%
    `if`(
      round,
      . %>% round(),
      .
    )
}
> maybe_round(5.3, TRUE)
Functional sequence with the following components:

 1. round(.)

Use 'functions' to extract the individual functions. 
> maybe_round(5.3, FALSE)
[1] 5.3

This second case is working correctly; it's returning the original value unchanged. But this first case is not, it's returning the magrittr pipeline, but not actually feeding it with 5. How can I get this to work as I intend? I assume this has something to do with how magrittr rewrites the syntax tree, but I can't quite work it out.


Solution

  • The syntax . %>% round(.) means function(.) round(.). Any time dot starts a pipeline it defines a function rather than being an ordinary pipeline. Put parentheses around the dot to prevent the dot from starting the inner pipeline.

     maybe_round = function(x, round = TRUE){
       x %>%
         `if`(
           round,
           (.) %>% round(),
           .
         )
     }
    
    maybe_round(5.3, TRUE)
    ## [1] 5
    

    Another possibility is to just leave it as a function and then evaluate that function at the outer dot like this:

     maybe_round = function(x, round = TRUE){
       x %>%
         `if`(
           round,
           (. %>% round())(.),
           .
         )
     }