How to modify dots and dispatch NextMethod

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

How to modify dots and dispatch NextMethod

Iñaki Úcar
Hi all,

Not sure if this belongs to R-devel or R-package-devel. Anyways...

Suppose we have objects of class c("foo", "bar"), and there are two S3
methods c.foo, c.bar. In c.foo, I'm trying to modify the dots and
forward the dispatch using NextMethod without any success. This is
what I've tried so far:

c.foo <- function(..., recursive=FALSE) {
  dots <- list(...)
  # inspect and modify dots
  # ...
  do.call(
    function(..., recursive=FALSE) structure(NextMethod("c"), class="foo"),
    c(dots, recursive=recursive)
  )
}

foobar <- 1
class(foobar) <- c("foo", "bar")
c(foobar, foobar)
Error: C stack usage  7970788 is too close to the limit

There's recursion (!). But the funniest thing is that if c.foo is
exported by one package and c.bar is exported by another one, there's
no recursion, but c.bar is never called (!!). Why is the same code
behaving in a different way depending on whether these functions are
defined in the .GlobalEnv or in two attached packages? (BTW,
isS3method() is TRUE, for c.foo and c.bar in both cases).

I'm blocked here. Am I missing something? Is there a way of doing
this? Thanks in advance.

Regards,
Iñaki

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to modify dots and dispatch NextMethod

Iñaki Úcar
I've set up a repo with a reproducible example of the issue described
in my last email:

https://github.com/Enchufa2/dispatchS3dots

Iñaki

2018-02-20 19:33 GMT+01:00 Iñaki Úcar <[hidden email]>:

> Hi all,
>
> Not sure if this belongs to R-devel or R-package-devel. Anyways...
>
> Suppose we have objects of class c("foo", "bar"), and there are two S3
> methods c.foo, c.bar. In c.foo, I'm trying to modify the dots and
> forward the dispatch using NextMethod without any success. This is
> what I've tried so far:
>
> c.foo <- function(..., recursive=FALSE) {
>   dots <- list(...)
>   # inspect and modify dots
>   # ...
>   do.call(
>     function(..., recursive=FALSE) structure(NextMethod("c"), class="foo"),
>     c(dots, recursive=recursive)
>   )
> }
>
> foobar <- 1
> class(foobar) <- c("foo", "bar")
> c(foobar, foobar)
> Error: C stack usage  7970788 is too close to the limit
>
> There's recursion (!). But the funniest thing is that if c.foo is
> exported by one package and c.bar is exported by another one, there's
> no recursion, but c.bar is never called (!!). Why is the same code
> behaving in a different way depending on whether these functions are
> defined in the .GlobalEnv or in two attached packages? (BTW,
> isS3method() is TRUE, for c.foo and c.bar in both cases).
>
> I'm blocked here. Am I missing something? Is there a way of doing
> this? Thanks in advance.
>
> Regards,
> Iñaki



--
Iñaki Úcar
http://www.enchufa2.es
@Enchufa2

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to modify dots and dispatch NextMethod

Tomas Kalibera

The example is invoking NextMethod via an anonymous function, which is
not allowed (see documentation for NextMethod). Normally one gets a
runtime error "'NextMethod' called from an anonymous function", but not
here as the anonymous function is called via do.call. I will fix so that
there is a runtime error in this case as well, thanks for uncovering
this problem.

I don't think there is a way to replace (unnamed) arguments in dots for
NextMethod.

Tomas

On 02/21/2018 02:16 PM, Iñaki Úcar wrote:

> I've set up a repo with a reproducible example of the issue described
> in my last email:
>
> https://github.com/Enchufa2/dispatchS3dots
>
> Iñaki
>
> 2018-02-20 19:33 GMT+01:00 Iñaki Úcar <[hidden email]>:
>> Hi all,
>>
>> Not sure if this belongs to R-devel or R-package-devel. Anyways...
>>
>> Suppose we have objects of class c("foo", "bar"), and there are two S3
>> methods c.foo, c.bar. In c.foo, I'm trying to modify the dots and
>> forward the dispatch using NextMethod without any success. This is
>> what I've tried so far:
>>
>> c.foo <- function(..., recursive=FALSE) {
>>    dots <- list(...)
>>    # inspect and modify dots
>>    # ...
>>    do.call(
>>      function(..., recursive=FALSE) structure(NextMethod("c"), class="foo"),
>>      c(dots, recursive=recursive)
>>    )
>> }
>>
>> foobar <- 1
>> class(foobar) <- c("foo", "bar")
>> c(foobar, foobar)
>> Error: C stack usage  7970788 is too close to the limit
>>
>> There's recursion (!). But the funniest thing is that if c.foo is
>> exported by one package and c.bar is exported by another one, there's
>> no recursion, but c.bar is never called (!!). Why is the same code
>> behaving in a different way depending on whether these functions are
>> defined in the .GlobalEnv or in two attached packages? (BTW,
>> isS3method() is TRUE, for c.foo and c.bar in both cases).
>>
>> I'm blocked here. Am I missing something? Is there a way of doing
>> this? Thanks in advance.
>>
>> Regards,
>> Iñaki
>
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to modify dots and dispatch NextMethod

Iñaki Úcar
2018-02-22 10:29 GMT+01:00 Tomas Kalibera <[hidden email]>:
>
> The example is invoking NextMethod via an anonymous function, which is not
> allowed (see documentation for NextMethod).

Thanks for your response. I definitely missed that bit.

> Normally one gets a runtime
> error "'NextMethod' called from an anonymous function", but not here as the
> anonymous function is called via do.call. I will fix so that there is a
> runtime error in this case as well, thanks for uncovering this problem.

Then I did well chosing this list! Please also note that you could
take that anonymous function out of the method and name it, and the
behaviour would be the same. So maybe this case should issue an error
too.

> I don't think there is a way to replace (unnamed) arguments in dots for
> NextMethod.

That's a pity. IMHO, it should be some mechanism for that, but dots
are special in inscrutable ways.

Anyway, for anyone insterested, I found a workaround:

https://github.com/Enchufa2/dispatchS3dots#workaround

>
> Tomas
>
>

Iñaki

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to modify dots and dispatch NextMethod

Tomas Kalibera
On 02/22/2018 12:07 PM, Iñaki Úcar wrote:

> 2018-02-22 10:29 GMT+01:00 Tomas Kalibera <[hidden email]>:
>> The example is invoking NextMethod via an anonymous function, which is not
>> allowed (see documentation for NextMethod).
> Thanks for your response. I definitely missed that bit.
>
>> Normally one gets a runtime
>> error "'NextMethod' called from an anonymous function", but not here as the
>> anonymous function is called via do.call. I will fix so that there is a
>> runtime error in this case as well, thanks for uncovering this problem.
> Then I did well chosing this list! Please also note that you could
> take that anonymous function out of the method and name it, and the
> behaviour would be the same. So maybe this case should issue an error
> too.
I am not sure I understand how, but if you find a way to bypass the new
check for an anonymous function (I intend to commit tomorrow), I will be
happy to have a look if you provide a reproducible example.
>> I don't think there is a way to replace (unnamed) arguments in dots for
>> NextMethod.
> That's a pity. IMHO, it should be some mechanism for that, but dots
> are special in inscrutable ways.
>
> Anyway, for anyone insterested, I found a workaround:
>
> https://github.com/Enchufa2/dispatchS3dots#workaround
Even though technically this won't be too hard, I don't think NextMethod
should be made any more complex than it is now. There should always be a
way to implement special dispatch scenarios in R and your workaround
shows it is possible specifically in your scenario.

Tomas

>> Tomas
>>
>>
> Iñaki

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to modify dots and dispatch NextMethod

Iñaki Úcar
2018-02-22 12:39 GMT+01:00 Tomas Kalibera <[hidden email]>:

> On 02/22/2018 12:07 PM, Iñaki Úcar wrote:
>>
>> 2018-02-22 10:29 GMT+01:00 Tomas Kalibera <[hidden email]>:
>>>
>>> The example is invoking NextMethod via an anonymous function, which is
>>> not
>>> allowed (see documentation for NextMethod).
>>
>> Thanks for your response. I definitely missed that bit.
>>
>>> Normally one gets a runtime
>>> error "'NextMethod' called from an anonymous function", but not here as
>>> the
>>> anonymous function is called via do.call. I will fix so that there is a
>>> runtime error in this case as well, thanks for uncovering this problem.
>>
>> Then I did well chosing this list! Please also note that you could
>> take that anonymous function out of the method and name it, and the
>> behaviour would be the same. So maybe this case should issue an error
>> too.
>
> I am not sure I understand how, but if you find a way to bypass the new
> check for an anonymous function (I intend to commit tomorrow), I will be
> happy to have a look if you provide a reproducible example.

I meant with a named function inside do.call, instead of an anonymous
one. For example:

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(
    c.foo.proxy,
    c(dots, recursive=recursive)
  )
}

c.foo.proxy <- function(..., recursive=FALSE)
structure(NextMethod("c"), class="foo")

Right now, the effect of the code above is the same as with the
anonymous function. Shouldn't it issue a similar error then?

>>>
>>> I don't think there is a way to replace (unnamed) arguments in dots for
>>> NextMethod.
>>
>> That's a pity. IMHO, it should be some mechanism for that, but dots
>> are special in inscrutable ways.
>>
>> Anyway, for anyone insterested, I found a workaround:
>>
>> https://github.com/Enchufa2/dispatchS3dots#workaround
>
> Even though technically this won't be too hard, I don't think NextMethod
> should be made any more complex than it is now. There should always be a way
> to implement special dispatch scenarios in R and your workaround shows it is
> possible specifically in your scenario.

My only concern about this workaround is that it triggers the dispatch
stack again from the beginning of the class hierarchy, which seems not
very elegant nor efficient.

>
> Tomas
>

Iñaki

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: How to modify dots and dispatch NextMethod

Tomas Kalibera
On 02/22/2018 02:31 PM, Iñaki Úcar wrote:

> 2018-02-22 12:39 GMT+01:00 Tomas Kalibera <[hidden email]>:
>> On 02/22/2018 12:07 PM, Iñaki Úcar wrote:
>>> 2018-02-22 10:29 GMT+01:00 Tomas Kalibera <[hidden email]>:
>>>> The example is invoking NextMethod via an anonymous function, which is
>>>> not
>>>> allowed (see documentation for NextMethod).
>>> Thanks for your response. I definitely missed that bit.
>>>
>>>> Normally one gets a runtime
>>>> error "'NextMethod' called from an anonymous function", but not here as
>>>> the
>>>> anonymous function is called via do.call. I will fix so that there is a
>>>> runtime error in this case as well, thanks for uncovering this problem.
>>> Then I did well chosing this list! Please also note that you could
>>> take that anonymous function out of the method and name it, and the
>>> behaviour would be the same. So maybe this case should issue an error
>>> too.
>> I am not sure I understand how, but if you find a way to bypass the new
>> check for an anonymous function (I intend to commit tomorrow), I will be
>> happy to have a look if you provide a reproducible example.
> I meant with a named function inside do.call, instead of an anonymous
> one. For example:
>
> 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(
>      c.foo.proxy,
>      c(dots, recursive=recursive)
>    )
> }
>
> c.foo.proxy <- function(..., recursive=FALSE)
> structure(NextMethod("c"), class="foo")
>
> Right now, the effect of the code above is the same as with the
> anonymous function. Shouldn't it issue a similar error then?
Yes, it will also result in runtime error after the change is committed:

calling c.foo...
Error in NextMethod("c") : 'NextMethod' called from an anonymous function

>>>> I don't think there is a way to replace (unnamed) arguments in dots for
>>>> NextMethod.
>>> That's a pity. IMHO, it should be some mechanism for that, but dots
>>> are special in inscrutable ways.
>>>
>>> Anyway, for anyone insterested, I found a workaround:
>>>
>>> https://github.com/Enchufa2/dispatchS3dots#workaround
>> Even though technically this won't be too hard, I don't think NextMethod
>> should be made any more complex than it is now. There should always be a way
>> to implement special dispatch scenarios in R and your workaround shows it is
>> possible specifically in your scenario.
> My only concern about this workaround is that it triggers the dispatch
> stack again from the beginning of the class hierarchy, which seems not
> very elegant nor efficient.
There may be a more elegant way, but that'd be a question for R-help and
it might be worth giving a broader context for what you want to achieve.
Also please note that S3 dispatch is done on the first argument, and c()
gives no special meaning to its first argument, what if e.g. the second
argument is of class "foo" but the first is not - is S3/NextMethod
really a good fit here?

Tomas

>
>> Tomas
>>
> Iñaki

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel