Here’s another fun but entirely useless exercise that shows some of the flexibility of R’s infix operators. C++ (and many other languages) have a ternary if-else operatory. You can write the following:
predicate ? value_if_true : value_if_false
“If Predicate then value_if_true, otherwise value_if_false”.
In R, we have ifelse
, or dplyr::if_else
, and we can also put simple if
on single lines:
if (predicate) value_if_true else value_if_false
But if we want, we can abuse the ability to make infix functions as so.
First we define a two-argument function, %?%
that takes a predicate on the left, and some expression on the right.
`%?%` <- function(lhs, rhs) {
rhs_prime <- substitute(rhs)
list(payload = rhs_prime, predicate = lhs)
}
We have to substitute()
the rhs
, because we don’t want R to evaluate that code straight away, we want to be able to make that decision once we’ve evaluated the predicate.
Our return value is a list with two elements.
The frozen rhs
value as payload
and the predicate value passed in as lhs
.
Then we define another function, %:%
that handles the if
part:
`%:%` <- function(lhs, rhs) {
predicate <- lhs$predicate
if (predicate) {
eval(lhs$payload)
} else {
rhs
}
}
Here, we just unpack the input from the lhs
and decide which branch to evaluate based on the predicate
.
In action:
(4 < 10) %?% "lower" %:% "higher"
## [1] "lower"
(4 > 10) %?% "lower" %:% "higher"
## [1] "higher"
And because we used substitute()
we only ever evaluate the value that matches the right branch.
So if one of the branches raises an error, but we never go down that branch, the expression still works:
(4 > 10) %?% stop("Error never reached") %:% "everything is fine"
## [1] "everything is fine"