tidyverse / purrr

A functional programming toolkit for R
https://purrr.tidyverse.org/
Other
1.27k stars 271 forks source link

Compose hits infinite recursion with custom "invisibly" adverb #1122

Closed byronvickers closed 1 month ago

byronvickers commented 5 months ago

When implementing a custom adverb to make return values invisible, composition of this adverb is somehow causing an infinite recursion.

library(purrr)

invisibly = function(.f){
  function(...){
    invisible(.f(...))
  }
}

compose(invisibly, quietly)(sum)(1:3)
#> Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
#> Error during wrapup: evaluation nested too deeply: infinite recursion / options(expressions=)?
#> Error: no more error handlers available (recursive errors?); invoking 'abort' restart

However, invisibly(quietly(sum))(1:3) performs as expected (i.e. invisibly returns the output of quietly(sum)(1:3)).

This seems possibly related to https://github.com/tidyverse/purrr/issues/828 but the end result there is unexpected output rather than an infinite recursion. Still, I imagine this issue would also be resolved by https://github.com/tidyverse/purrr/issues/651; is that work still planned or has it been shelved?

Apologies if I've missed something in the docs or issues that would explain why I'm getting this behaviour!

hadley commented 1 month ago

I can't say I know why this is happening off the top of my head, but the problem is that you didn't force(.f). If you do that (as you should do with all the arguments to a function factory), the problem goes away:

library(purrr)

invisibly = function(.f){
  force(.f)
  function(...){
    invisible(.f(...))
  }
}

compose(invisibly, quietly)(sum)(1:3)
(compose(invisibly, quietly)(sum)(1:3))
#> $result
#> [1] 6
#> 
#> $output
#> [1] ""
#> 
#> $warnings
#> character(0)
#> 
#> $messages
#> character(0)

Created on 2024-07-15 with reprex v2.1.0