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?

Jouni Helske
  • 6,427
  • 29
  • 52
Patrick Roocks
  • 3,129
  • 3
  • 14
  • 28
  • 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. – Jouni Helske Mar 15 '13 at 12:29
  • 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! – Patrick Roocks Mar 15 '13 at 12:36

2 Answers2

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.

Jouni Helske
  • 6,427
  • 29
  • 52
  • 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. – Patrick Roocks Mar 15 '13 at 12:43
  • @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"`. – QkuCeHBH Mar 15 '13 at 12:49
  • Nice, so even though `class` function gives `function` as a class of a function it isn't really a member of `function` class :) – Jouni Helske Mar 15 '13 at 12:49
  • Yes, `str(a)` before and after `class(a)<-"function"` confirms this. – Jouni Helske Mar 15 '13 at 12:50
  • 3
    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. – Patrick Roocks Mar 15 '13 at 14:25
  • 2
    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. – hadley Mar 15 '13 at 19:02
  • @hadley 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? – Josh O'Brien Mar 16 '13 at 06:21
  • 2
    @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. – hadley Mar 18 '13 at 01:29
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
QkuCeHBH
  • 960
  • 1
  • 9
  • 23
  • This produces same error for me as the `"+.function"` approach. – Jouni Helske Mar 15 '13 at 12:30
  • Sorry, forgot to replace `+` with `%+%`... now it should work – QkuCeHBH Mar 15 '13 at 12:34
  • Thanks, that seemed bit strange first, though I misunderstood the meaning of %+%.. – Jouni Helske Mar 15 '13 at 12:35
  • 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... – Patrick Roocks Mar 15 '13 at 12:45
  • @Patrick Yes, I agree. However, even standard R uses `%...%` functions (*e.g.* `%*%` for matrix multiplication or `%/%` for integer division). – QkuCeHBH Mar 15 '13 at 12:50