# 15 S4

## 15.1 Classes

1. Q: What happens if you define a new S4 class that doesn’t “contain” an existing class? (Hint: read about virtual classes in ?setClass.)

A: It depends on the other arguments. If we supply a class that doesn’t exist, we’ll get an error

setClass("Programmer", slots = c(skill = "ANY"), contains = "Human")
#> Error in reconcilePropertiesAndPrototype(name, slots, prototype, superClasses, : no definition was found for superclass "Human" in the specification of class "Programmer"

However, we can get around that, if we register the class before

setOldClass("Human")
.Programmer <- setClass("Programmer", slots = c(Skill = "ANY"), contains = "Human")

Supplying neither slots, nor contains results in a contructor for virtual classes

.VirtualProgrammer <- setClass("VirtualProgrammer")
# The same as contains = "VIRTUAL" (here you could also supply slots)
.VirtualProgrammer <- setClass("VirtualProgrammer", contains = "VIRTUAL")

Just leaving out contains, but supplying slots results in a constructor without superclass

.DataScientist <- setClass("RProgrammer", slots = c(stats = "ANY",
math = "ANY",
programming = "ANY"))
2. Q: Imagine you were going to reimplement ordered factors, dates, and data frames in S4. Sketch out the setClass() calls that you would use to define the classes. What should they inherit from? What slots should they use?

A: The basic idea is to use a slot for the base type and one slot per attribute. Inheritance matters for ordered factors and dates. Special checks like equal lengths of list elements for columns of a data frame should be done within a validator.

## 15.2 Generics and methods

1. Q: In the defintion of the generic, why is it necessary to repeat the name of the generic twice?

A: The first time it is needed as name of the generic and the second time it is needed to explicitly incorporate method dispacth via standardGeneric() within the generic’s body (def parameter). This is similar to UseMethod() within S3.

2. Q: What’s the difference between the generics generated by these two calls?

setGeneric("myGeneric", function(x) standardGeneric("myGeneric"))
setGeneric("myGeneric", function(x) {
standardGeneric("myGeneric")
})

A: The first call defines a standard generic and the second one creates a nonstandard generic. One can confirm this directly whlie printing (showing in S4 jargon) the function.

setGeneric("myGeneric", function(x) standardGeneric("myGeneric"))
#> [1] "myGeneric"
myGeneric
#> standardGeneric for "myGeneric" defined from package ".GlobalEnv"
#>
#> function (x)
#> standardGeneric("myGeneric")
#> <environment: 0x4b38780>
#> Methods may be defined for arguments: x
#> Use  showMethods("myGeneric")  for currently available ones.

setGeneric("myGeneric", function(x) {
standardGeneric("myGeneric")
})
#> [1] "myGeneric"
myGeneric
#> nonstandardGenericFunction for "myGeneric" defined from package ".GlobalEnv"
#>
#> function (x)
#> {
#>     standardGeneric("myGeneric")
#> }
#> <environment: 0x4cabfc8>
#> Methods may be defined for arguments: x
#> Use  showMethods("myGeneric")  for currently available ones.
3. Q: What happens if you define a method with different argument names to the generic?

A: It depends. Lets first create the object hadley of class “Person”:

.Person <- setClass("Person",
slots = c(
name = "character",
age = "numeric"
)
)

#> An object of class "Person"
#> Slot "name":
#>
#> Slot "age":
#> numeric(0)

Now let us see, which arguments can be supplied to the show() generic

formals("show")
#> \$object

Usually we would use this argument when defining a new method

setMethod("show", "Person",
function(object){
cat(object@name, "creates hard exercises")
})
#> Hadley creates hard exercises

When we supply another name, for example x instead of object, as a first element of our method, this becomes matched to the correct object argument and we get a warning. However, our method will work

setMethod("show", "Person",
function(x){
cat(x@name, "creates hard exercises")
})
#> Warning: For function 'show', signature 'Person': argument in method
#> definition changed from (x) to (object)
#> Hadley creates hard exercises

If we add more arguments to our method than our generic can handle, we will get an error

setMethod("show", "Person",
function(x, y){
cat(x@name, "is", x@age, "years old")
})
#> Error in conformMethod(signature, mnames, fnames, f, fdef, definition): in method for 'show' with signature 'object="Person"': formal arguments (object = "Person") omitted in the method definition cannot be in the signature

However, if we do this with arguments added to the correctly written object argument, we will get the informative error message, that we could in general add other argument names for generics, which can take the ... argument

setMethod("show", "Person",
function(object, y){
cat(object@name, "is", object@age, "years old")
})
#> Error in rematchDefinition(definition, fdef, mnames, fnames, signature): methods can add arguments to the generic 'show' only if '...' is an argument to the generic
4. Q: What other ways can you find help for a method? Read ?"?" and summarise the details.

A: We can get

• general documentation of the generic via ?genericName
• general documentation of the methods from a generic via methods?genericName
• documentation of a specific method via ClassName?methodName

Regarding the latter, we can also get help on a specific method via adding a ? in front of a function call, like ?show(hadley).

## 15.3 Method dispatch

1. Q: Take the last example which shows multiple dispatch over two classes that use multiple inheritance. What happens if you define a method for all terminal classes? Why does method dispatch not save us much work here?

A: We will introduce ambiguity, since one class has distance 2 to all terminal nodes and the other 4 have distance 1 to two terminal nodes each. To resolve this ambiguity we have to define 5 more methods, one per class combination.