C. Lente

O novo pipe chegou

A profecia estava correta: o R finalmente ganhou um pipe próprio, embutido à linguagem em si. Mais de 7 anos depois do primeiro commit do {magrittr}, o %>%, operador com o qual compartilhamos tantas boas memórias, símbolo do R moderno e inspiração para o logo da Curso-R, vai se tornar obsoleto. De acordo com a agenda do R Project, no badalar da meia-noite do dia 18/05/2021 foi lançada oficialmente a versão 4.1.0 (“Camp Pontanezen”) do R, contendo, dentre outras novidades, suporte para o operador |>.

Devo admitir que estou um pouco triste com a despedida, como se eu estivesse prestes a mudar de casa: pode ser que a casa nova seja melhor e maior, mas, de uma forma ou de outra, estou abandonando um lugar que foi palco de várias boas memórias por um lugar estéril e inerte…

Só que mesmo assim nós seguimos em frente e mudamos de casa! As lembranças não vão embora e temos a oportunidade de criar mais boas memórias com esse novo plano de fundo. Sendo assim, vamos entender as principais mudanças do R 4.1 e, principalmente, como usar o novo pipe.

O novo pipe: |>

À primeira vista, o novo pipe é igual ao antigo: usado no final de uma linha para passar o resultado do que está à sua esquerda como primeiro argumento da linha debaixo. Aqui temos um exemplo simples do uso mais comum dos pipes:

library(dplyr)

# Uma pipeline com o pipe novo
starwars |>
  group_by(species, sex) |>
  select(species, sex, height) |>
  summarise(height = mean(height, na.rm = TRUE)) |>
  pull(height) |>
  summary()
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>    66.0   163.0   181.3   173.5   196.0   264.0

As diferenças aparecem quanto tentamos usar o ., a saber, o dot placeholder. Bem comum no antigo pipe, o uso do . para escolher onde a substituição deve ocorrer é simplesmente proibido no novo pipe. Essa decisão foi tomada porque o . gera situações ambíguas como a descrita a seguir:

# Vamos gerar essa string a partir de "-" %>% paste0()
"-><-"
#> [1] "-><-"

# Podemos começar colando o hífen na esquerda
"-" %>%
  paste0(">")
#> [1] "->"

# Agora adicionamos o . para colar na direita também...
# Mas isso dá errado!
"-" %>%
  paste0("><", .)
#> [1] "><-"

# Precisamos colocar o . em dois lugares diferentes,
# incluindo, contraintuitivamente, no primeiro argumento
"-" %>%
  paste0(., "><", .)
#> [1] "-><-"

Assim, em troca de nunca mais precisar carregar o {magrittr}, usar usethis::use_pipe() ou se preocupar com o ambiente do {future}, vamos ser obrigados a criar funções parciais que sempre esperam a substituição no seu primeiro argumento. Mas, falando em funções, chegamos à segunda maior novidade do R 4.1…

Funções anônimas: \(x)

Se você se acostumou com a notação de fórmulas do {purrr}, então funções anônimas não são nada de novo. Também conhecidas como lambdas, funções anônimas não passam de funções que não precisam receber um nome, ou seja, podem ser utilizadas pelo programa na hora de sua criação. No caso do {purrr} isso se dava através de uma gambiarra feita em cima das fórmulas (~.x), mas, ainda assim, isso era melhor do que a sintaxe bastante prolixa que o R básico exigia (function(x) x).

A nova sintaxe é, na minha opinião, bastante melhor que a estratégia do {purrr}. Apesar de ela ser um pouco mais extensa, ela funciona em todos os lugares (não só no tidyverse), permite nomes arbitrários para os argumentos e não bagunça a função do ~. A nova função anônima é declarada com uma barra oblíqua para a esquerda seguida por todos os argumentos entre parênteses e o código; essencialmente, é igual à notação antiga, mas function vira \. A escolha do símbolo \ também é ótima, pois é igual à de outras linguagens e porque lembra um λ (lambda) com a perna cortada. Para comparar todas as diferentes notações, segue um breve exemplo (saídas suprimidas):

library(purrr)

# Sem função anônima
soma_um <- function(x) {
  x + 1
}
map(1:10, soma_um)

# A notação antiga do {base} é extensa
map(1:10, function(x) x + 1)

# A notação do {purrr} força o .x
map(1:10, ~ .x + 1)

# A notação nova permite qualquer nome
map(1:10, \(n) n + 1)

# A notação nova funciona fora do tidyverse
lapply(1:10, \(x) x + 1)

# Para mais de 2 argumentos, o {purrr} muda
pmap(list(1:10, 11:20, 21:30), ~ ..1 + ..2 + ..3)

# A notação nova continua consistente
pmap(list(1:10, 11:20, 21:30), \(x, y, z) x + y + z)

De quebra, na minha opinião, também será muito mais fácil de ensinar o \(x) do que o ~.x. A notação é intuitiva, consistente e funciona fora do tidyverse, então, para mim, o novo lambda é até mais interessante do que o novo pipe!

Miscelânea

Para te poupar desse trabalho, eu li o NEWS da nova versão a fim de saber quais são as novidades mais interessantes do 4.1. Fora o novo pipe e o novo lambda, achei algumas coisas dignas de nota:

  • Usar c() para combinar fatores agora retorna um fator que também combina todos os níveis (de forma similar ao forcats::fct_c());

  • apply() agora tem um argumento simplify para desabilitar a simplificação de resultados;

  • O novo utilitário ...names() retorna os nomes dos argumentos passados via ... (possivelmente facilitando o trabalho do {tidyselect});

  • As funções URLencode() e URLdecode() agora funcionam com vetores de URIs;

  • grep(), sub(), regexp(), etc. ficaram mais rápidas para fatores longos com poucos níveis e

  • duplicated() e anyDuplicated() agora são otimizados para vetores numéricos já ordenados via ALTREP.

Conclusão

Se você não consegue esperar para testar as novas funcionalidades, já é possível baixar a versão beta do R 4.1 diretamente do site do R Project. O time do R Core apenas pede que você reporte quaisquer bugs detectados!

Para quem é mais paciente e prefere esperar o lançamento oficial na sua plataforma de preferência, abra o seu RStudio, aperte CTRL + SHIFT + M com carinho e agradeça por todas as aventuras patrocinadas pelo pipe. O pacote {magrittr} continuará lá, funcionando, mas cada vez menos relevante… Apesar de estarmos entrando em uma nova fase do R, as lembranças e aprendizados do passado nunca irão embora.

%>%