Kun Ren
Kun Ren

Reputation: 5003

Is it possible to define operator without %%?

In R user-defined operators are allowed, but it seems that only % % like operators are accepted. Is it possible to walk around this restriction to define operators like, for example, >>, or something that is not like % %?

The operator must be a real operator so that we can use it like 1 >> 2 and do not have to use it like ">>"(1,2).

Upvotes: 13

Views: 898

Answers (2)

Konrad Rudolph
Konrad Rudolph

Reputation: 545953

No. R only allows you to

  1. redefine existing operators (such as `+` or, indeed, `<-`), and
  2. define new infix operators by surrounding them with %…%.

These are the rules we have to play by. However, inside these rules all is fair game. For instance, we can redefine `+` for character strings to perform concatenation, without destroying its normal meaning (addition):

`+`
# function (e1, e2)  .Primitive("+")

This is the old definition, which we want to preserve for numbers:

`+.default` = .Primitive('+')
`+.character` = paste0`1
`+` = function (e1, e2) UseMethod('+')
1 + 2
# [1] 3
'hello' + 'world'
# [1] "helloworld"

This exploits the S3 class system in R to make `+` fully generic on the type of its first argument.

The list of operators that can thus be redefined is quite eclectic. At first count, it contains the following operators:

+, -, *, /, ^, &, |, :, ::, :::, $, =, <-, <<-, ==, <, <=, >, >=, !=, ~, &&, ||, !, ?, @, :=, (, {, [, [[

(From the {modules} source code.)

In this list, one particular operator is noteworthy: `:=` is an overridable operator (and {data.table} for example uses it) but unlike most the other operators in this list it has no default implementation.

Similarly, you can define assignment versions of pretty much every operator, not just those with a predefined assignment (such as `[<-`). For example, `(<-` and `{<-` are also missing a default implementation. This is why the following code fails:

a = 1
(a) = 2
# Error in (a) = 2 : could not find function "(<-"
{a} = 3
# Error in { : could not find function "{<-"

But the code can be made to work by defining the operators:

`(<-` = `{<-` = function (x, value) value

The same works for every function, including almost all operators1, and even control structures (see the comments below this answer).

By contrast, `**` isn’t a real operator: it’s a syntactic alias for `^`. Users can define their own `**` function but it can’t be called via the code a ** b so it’s not an overridable operator (except via overriding `^`).

In the same vein, you can override -> (only) by redefining <-. This seems like it would rarely make sense — but at least one good example of this exists, to define less verbose lambdas:

> sapply(1 : 4, x -> 2 * x)
[1] 2 4 6 8

Implementation as a gist


1 The only exception are the assignment operators: You cannot change the meaning of a <- b <- c by redefining `<-<-` (and the same for `=<-`, `<<-` and `:=`), due to the language’s operator precedence and associativity rules. However, the following code invokes both `(<-` and `<-<-`, and fails if either of these is not defined:

(a <- b) <- c

Messed up.

Upvotes: 20

Rich Scriven
Rich Scriven

Reputation: 99361

You can do things like this, but you may want to assign these objects to a new environment to be safe.

> "^" <- function(x, y) `-`(x, y)  ## subtract y from x
> 5 ^ 3
# [1] 2

> "?" <- function(x, y) sum(x, y)  ## add x and y
> 5 ? 5
# [1] 10

Upvotes: 5

Related Questions