Konrad
Konrad

Reputation: 18585

Implementing basic arithmetic in S4 class object

I'm creating a S4 object of the money class in the following manner:

# Create class ------------------------------------------------------------

# Create S4 class object holding money and export to generator function
setClass(Class = "money",
         slots = list(currency = "character",
                      value = "numeric")) -> money

For which I'm later defining the show method:

# Methods -----------------------------------------------------------------

# Create show method
setMethod("show",
          "money",
          function(object) {
              cat(switch(object@currency,
                         GBP = intToUtf8(163)),
                  format(
                      x = round(object@value, 2),
                      trim = TRUE,
                      big.mark = ",",
                      big.interval = 3
                  ),
                  sep = "")
          })

Preview

So far it works as promised:

# Create test object
tst_fig <- new(Class = "money",
               value = 1e6,
               currency = "GBP")

# Show the object
tst_fig
# £1,000,000

Problem

I would like to enable basic arithmetic on that object:

>> tst_fig + 1e6
Error in tst_fig + 1000000 : non-numeric argument to binary operator

Desired results

> tst_fig + 1e6
# £2,000,000

Attempts

Naturally this is won't work:

>> setMethod("+",
...           "money",
...           function(object, x) {
...               object@value + x
...           })
Error in conformMethod(signature, mnames, fnames, f, fdef, definition) : 
  in method for ‘+’ with signature ‘e1="money"’: formal arguments (e1 = "money", e2 = "money") omitted in the method definition cannot be in the signature

Side notes

There is a similar excellent answer provided by @Roland on implementing money class in S3; in the context of this question I'm interested in creating S4 class that would behave in a similar manner without any specific reason other than curiosity. The key requirements is that isS4() on that object returns TRUE.

What I mean by similar manner:

It prints like nicely formatted money but permits all operations the one could do on a common numeric.

Upvotes: 3

Views: 247

Answers (1)

cdeterman
cdeterman

Reputation: 19960

I came across how to do this in my own question here. I have generally used the setMethod('Arith') approach as it is more concise when you intend to implement several operations. If you search the documentation ?Arith you will see that it list the different operations as well as other S4 group generics.

As the error suggests you need to have e1 and e2 defined for Arith methods. In your specific case the following works.

Note - to get your desired output (i.e. a money class object) you will need to create a new money object.

setMethod("+", 
    c(e1="money", e2="numeric"), 
    function(e1, e2){
        new(Class = "money", value = e1@value + e2, currency = e1@currency)
    }
)

tst_fig + 1e6
[1] £2e+06

However, as I said, you probably want the more general, concise version which uses .Generic to interpret the Arith method you are using.

setMethod("Arith", 
    c(e1="money", e2="numeric"),
    function(e1, e2)
    {
        op = .Generic[[1]]
        switch(op,
            `+` = return(new(Class = "money", value = e1@value + e2, currency = e1@currency))
        )
    }
)

Upvotes: 4

Related Questions