https://github.com/Enchufa2/dispatchS3dots
https://github.com/Enchufa2/dispatchS3dots
Last synced: 4 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/Enchufa2/dispatchS3dots
- Owner: Enchufa2
- Created: 2018-02-21T13:11:00.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2018-02-22T11:14:17.000Z (about 7 years ago)
- Last Synced: 2024-11-09T02:15:53.239Z (5 months ago)
- Language: R
- Size: 2.93 KB
- Stars: 2
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- jimsghstars - Enchufa2/dispatchS3dots - (R)
README
## Problem statement
This is a demo repository for the issue described in the [R-devel mailing list](https://stat.ethz.ch/pipermail/r-devel/2018-February/075613.html).
Suppose we have R objects of class `c("foo", "bar")`, and there are two S3 methods, `c.foo` and `c.bar`. In `c.foo`, we want to modify the arguments passed by `...` and forward the dispatch using `NextMethod`. The following code tries to solve this problem, but it results in an infinite recursion (!):
```r
c.foo <- function(..., recursive=FALSE) {
message("calling c.foo...")
dots <- list(...)
# inspect and modify dots; for example:
if (length(dots > 1))
dots[[2]] <- 2
do.call(
function(..., recursive=FALSE) structure(NextMethod("c"), class="foo"),
c(dots, recursive=recursive)
)
}c.bar <- function(..., recursive=FALSE) {
message("calling c.bar...")
NextMethod()
}foobar <- 1
class(foobar) <- c("foo", "bar")c(foobar, foobar)
#> calling c.foo...
#> ...
#> Error: C stack usage 7970788 is too close to the limit
```The very same methods are deployed into two packages included in this repo. Let's install them and try the same operation:
```r
rm(c.foo, c.bar)
devtools::install("package.foo")
devtools::install("package.bar")
library(package.foo)
library(package.bar)c(foobar, foobar)
#> calling c.foo...
#> [1] 1 2
#> attr(,"class")
#> [1] "foo"
```and there's no recursion this time, but `c.bar` was not called (!).
## Update
As [Tomas Kalibera pointed out](https://stat.ethz.ch/pipermail/r-devel/2018-February/075620.html), the documentation states that calling `NextMethod` from an anonymous function is not allowed (I missed that bit!), so this code should issue an error. Apparently, there is no way to replace (unnamed) arguments in dots for `NextMethod`.
## Workaround
A possible workaround for this is to reinitialise the dispatch stack by calling the generic again if any argument was modified, and finally call `NextMethod` cleanly. This works, but it means that `c.foo` will be called twice every time an argument is modified:
```r
c.foo <- function(..., recursive=FALSE) {
message("calling c.foo...")
modified <- FALSE
dots <- list(...)
# inspect and modify dots; for example:
if (length(dots > 1) && dots[[2]] != 2) {
dots[[2]] <- 2
modified <- TRUE
}
if (modified)
do.call(c, c(dots, recursive=recursive))
else structure(NextMethod(), class="foo")
}c(foobar, foobar)
#> calling c.foo...
#> calling c.foo...
#> calling c.bar...
#> [1] 1 2
#> attr(,"class")
#> [1] "foo"
```