tidyverse / magrittr

Improve the readability of R code with the pipe
https://magrittr.tidyverse.org
Other
957 stars 157 forks source link

Strange result using dot #197

Closed balwierz closed 5 years ago

balwierz commented 5 years ago

This is correct:

c("a", "b", "c") %>% .[1]
[1] "a"

This is unexpected:

c("a", "b", "c") %>% c(.[1])
[1] "a" "b" "c" "a"

I don't uderstand what has happened here. If the pipe is evaluated first, c("a", "b", "c") %>% c gives a,b,c, but then c("a", "b", "c") (.[1]) should be syntax error. If . is substituted then it is c("a", "b", "c") c("a", "b", "c")[1], which is still a syntax error.

Yes, c("a", "b", "c") %>% {c(.[1])} works fine. IMHO, c("a", "b", "c") %>% c(.[1]) should be either the expected behaviour or a syntax error with a warning to use parentheses. Otherwise it can cause a lot of trouble.

egnha commented 5 years ago

The behavior you observe is as expected.

The rule is that an expression like

x %>% f(<STUFF>)

is equivalent to

. <- x; f(<STUFF>)

whenever . appears as an argument of f(<STUFF>), otherwise

. <- x; f(., <STUFF>)

whenever . does not appear as an argument of f(<STUFF>). (The assignment . <- x happens in a temporary child environment. You can think of it as happening in a call to local().)

Thus x %>% .[1] has the same effect as . <- x; .[1], because the RHS .[1] is actually the call `[`(., 1) (first case), while x %>% c(.[1]) has the same effect as . <- x; c(., .[1]) (second case).

Incidentally, x %>% .[1] (first case) is the same as x %>% `[`(1) (second case).

balwierz commented 5 years ago

Thanks for the explanation

smbache commented 5 years ago

Another way to put it is that

c("a", "b", "c") %>% .[1]

translates to

`[`(c("a", "b", "c"), 1)

and

c("a", "b", "c") %>% c(.[1])

translates to

c(c("a", "b", "c") , c("a", "b", "c")[1])

the above is not actually what happens, so the lhs expression is only evaluated once.