R S3 Group Generics
A group generic is one method that answers a whole group of operators. An Ops method is named Ops with the class name after a dot.
Add two wallets
Ops.wallet answers the plus call and builds a new wallet from the two balances. The total prints 15 and the result still carries the wallet class.
Ops.wallet <- function(e1, e2) {
if (.Generic == "+") {
structure(list(balance = e1$balance + e2$balance), class = "wallet")
} else {
stop("wallets only add")
}
}
w1 <- structure(list(balance = 10), class = "wallet")
w2 <- structure(list(balance = 5), class = "wallet")
total <- w1 + w2
print(total$balance)
print(class(total))Subtraction is refused
The minus call reaches the same method with a different .Generic value. The method stops, tryCatch catches the stop, and the message prints as a plain value.
Ops.wallet <- function(e1, e2) {
if (.Generic == "+") {
structure(list(balance = e1$balance + e2$balance), class = "wallet")
} else {
stop("wallets only add")
}
}
w1 <- structure(list(balance = 10), class = "wallet")
w2 <- structure(list(balance = 5), class = "wallet")
result <- tryCatch(w1 - w2, error = function(e) {
conditionMessage(e)
})
print(result)Comparison reaches the same method
The equality operator belongs to the same group, so it lands in Ops.wallet too. The method refuses it with the same message.
Ops.wallet <- function(e1, e2) {
if (.Generic == "+") {
structure(list(balance = e1$balance + e2$balance), class = "wallet")
} else {
stop("wallets only add")
}
}
w1 <- structure(list(balance = 10), class = "wallet")
w2 <- structure(list(balance = 5), class = "wallet")
result <- tryCatch(w1 == w2, error = function(e) {
conditionMessage(e)
})
print(result)Add three wallets in a chain
Each addition returns a wallet, so the second plus call uses Ops.wallet again. The three balances add to 35.
Ops.wallet <- function(e1, e2) {
if (.Generic == "+") {
structure(list(balance = e1$balance + e2$balance), class = "wallet")
} else {
stop("wallets only add")
}
}
w1 <- structure(list(balance = 10), class = "wallet")
w2 <- structure(list(balance = 5), class = "wallet")
w3 <- structure(list(balance = 20), class = "wallet")
total <- w1 + w2 + w3
print(total$balance)Example
Ops.wallet <- function(e1, e2) {
if (.Generic == "+") {
structure(list(balance = e1$balance + e2$balance), class = "wallet")
} else {
stop("wallets only add")
}
}
purse <- structure(list(balance = 10), class = "wallet")
gift <- structure(list(balance = 5), class = "wallet")
topup <- structure(list(balance = 20), class = "wallet")
total <- purse + gift + topup
print(total$balance)
result <- tryCatch(total - gift, error = function(e) {
conditionMessage(e)
})
print(result)Key points
- R calls the Ops method for the arithmetic, comparison and logical operators on the class.
- .Generic holds the text name of the operator that was called.
- The method reads .Generic and chooses what to do for that operator.
- An operator the method does not allow can stop with its own message.
- The answer to an allowed operator can carry the class again, so the result joins the next operation.
In this example, three wallets add into a total of 35 because each addition returns a wallet. The subtraction after it reaches the same Ops method and its refusal is caught and printed.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท rdrr.io R Snippets
Exercise
Inside Ops.wallet, how does the method know which operator was called?