pipe into return doesn't work as expected

Viewed 40

The following chunks of code should be doing exactly the same thing, but the first chunk unexpectedly prints "We reach here?" also when x = 2, which shouldn't be the case (I hope it's obviosu what I mean).

Chunk 1

library(tidyverse)
input <- c(1,2,3)
output <- map(input, function(x) {
  print(x)
  if (x == 2) {
    return(x)
  }
  print('We reach here?')
  return(x)
})

Output

This output is as expected, as the string doesn't print when x = 2.

[1] 1
[1] "We reach here?"
[1] 2
[1] 3
[1] "We reach here?"

Chunk 2

input <- c(1,2,3)
output <- map(input, function(x) {
  print(x)
  if (x == 2) {
    x %>% return() 
  }
  print('We reach here?')
  x %>% return()
})

Output

[1] 1
[1] "We reach here?"
[1] 2
[1] "We reach here?"
[1] 3
[1] "We reach here?"

What am I missing here? Is this a bug?

1 Answers

The return() function jumps up a level. But the magrittr pipe %>% pushes evaluation down a level using some tricky evaluation. So piping into return() jumps back to where it started, instead of up a level from the function where you wrote it.

I would have expected the base pipe x |> return() to work, because it really should be identical to return(x) (whereas the magrittr pipe is only a partial simulation of that code). However, it is not allowed for some reason.

Is this a bug? It's a limitation of the magrittr pipe for sure. I'm not sure if the magrittr documentation discusses it. (It does; see the edit below!) So avoid putting return() in a pipe, and just use

return(x)

Edited to add:

I looked carefully through the magrittr docs, and found that this is discussed in the "tradeoffs" vignette. They give alternative versions of the pipe, %!>% with "eager" semantics and %|>% with "nested" semantics to handle this. (The %|> pipe doesn't appear to be in magrittr currently, but it should match the base pipe fairly closely.)

input <- c(1,2,3)
output <- map(input, function(x) {
  print(x)
  if (x == 2) {
    x %!>% return() 
  }
  print('We reach here?')
  x %!>% return()
})

This prints the same output as your Chunk 1.

Related