Operator overloading for functions in R - strange behavior
Asked Answered
C

2

8

Unfortunately things like (f+g)(3) where f and g are both unary functions do not work in R. Hence I tried to overload the "+" operator for unary functions in the following way:

"+.function" = function(e1, e2){
  return(function(x) e1(x) + e2(x))
}

But if I try to use this, this does nothing. The code

 a = function(x) 2*x
 (a+a)(2)

produces the same error as if +.function is not even defined.

By some time playing around I found out that there is in fact a possibility to add functions in this way: If the functions are member functions of an reference class, this works! I.e., the following code (together with the "+" definition from above)

clsA = setRefClass("clsA", 
  methods = list(
    b = function(x) 2*x
  ))

inst_a = clsA$new()
(inst_a$b + inst_a$b)(2)

returns "8" (as expected). Hence I already have some kind of a workaround for my problem. Now my questions are:

What is the reason for this strange behavior? Why doesn´t +.function care about "usual" function but class member functions? Has anyone an idea how "expand" the operator to usual functions?

Cashbox answered 15/3, 2013 at 12:17 Comment(2)
If you redifine the class of a, for example like class(a)<-"test", and make your "+.function" as "+.test", then (a+a)(2) works. So it seems that the function class is somehow special.Cerography
Nice, this works :-) Well, one could consider this still as a workaround, but I think it is a much "smarter" workaround than my idea with the reference class. Thank you very much for this idea!Cashbox
C
5

If you redifine the class of a, for example like class(a)<-"ownfunction" (or better yet class(a)<-c("ownfunction","function"), and make your "+.function" as "+.ownfunction", then (a+a)(2) works.

It seems that the function class is treated in some special way: If you run debug("+.function");(a+a)(2) you see that "+.function" is not even called.

EDIT: see comments.

Cerography answered 15/3, 2013 at 12:38 Comment(8)
Based on your idea: If I redefine class(a)<-"function" than my original +.function operator works for a as well as for class methods. I think this would be the smartest solution.Cashbox
@Patrick This is true. I belive this happens because after class(a) <- "function", a has the class attribute "function" which is not the case for a normal function. However, in both cases class(a) returns "function".Candescent
Nice, so even though class function gives function as a class of a function it isn't really a member of function class :)Cerography
Yes, str(a) before and after class(a)<-"function" confirms this.Cerography
Another remark on this solution: To allow also expressions like (f+g+h)(2) one should use { res = function(x) e1(x) + e2(x); class(res) = "function"; return(res) } Additionally: Using ... instead of x allows also for n-ary functions, i.e. (u+v)(2,2). And replacing + by Ops and .Primitive(.Generic) respectively generalizes the solution.Cashbox
The reason why this happens is that is.object(function() {}) is FALSE so internal S3 dispatch does not occur. This is a performance optimisation to avoid the overhead of method dispatch when working with basic types.Popish
@Popish That's really cool to know. Until I read your comment, I was having a devil of a time understanding the results of the following: setMethod("Ops", c("function", "function"), function(e1, e2) 999); coerce + coerce; mean + mean. (Unfortunately it does mean there's no nice S4 answer to the OP's question.) I see that is.object(new.env()) and is.object(call("mean")) both return FALSE. Do you know of an easy place to see which basic types have their OBJECT bit set and which don't?Reposition
@JoshO'Brien none of the basic types of the OBJECT bit set. You either have to add a class attribute, or use an S4 object.Popish
C
3

As a workaround, you could define a special operator (%...%) like this:

"%+%" <- function(e1, e2) {
  return(function(x) e1(x) + e2(x))
}

a <- function(x) 2*x
(a %+% a)(2) # == 8
Candescent answered 15/3, 2013 at 12:27 Comment(5)
This produces same error for me as the "+.function" approach.Cerography
Sorry, forgot to replace + with %+%... now it should workCandescent
Thanks, that seemed bit strange first, though I misunderstood the meaning of %+%..Cerography
Sure this works; but %+% looks not so smart. And generalizing it to -, *, /,... would produce many lines of code. With the ideas above also Ops.function is possible, generalizing this operator for +, -, *, etc...Cashbox
@Patrick Yes, I agree. However, even standard R uses %...% functions (e.g. %*% for matrix multiplication or %/% for integer division).Candescent

© 2022 - 2024 — McMap. All rights reserved.