4 Methods (part 1)

4.1 Introduction

We describe our methods in this chapter.

4.2 Improving `toss()`

From chapter 2, we ended up with the following `toss()` function:

``````#' @title Coin toss function
#' @description Simulates tossing a coin a given number of times
#' @param x coin object (a vector)
#' @param times number of tosses
#' @param prob vector of probabilities for each side of the coin
#' @return vector of tosses
toss <- function(x, times = 1, prob = NULL) {
sample(x, size = times, replace = TRUE, prob = prob)
}``````

The issue with the way `toss()` has been defined so far, is that you can pass it any type of vector (not necessarily of class `"coin"`), and it will still work:

``````toss(c('tic', 'tac', 'toe'))
#> [1] "tac"``````

To create a function `toss()` that only works for objects of class `"coin"`, we could add a `stop()` condition that checks if the argument `x` is of the right class:

``````toss <- function(x, times = 1, prob = NULL) {
if (class(x) != "coin") {
stop("\ntoss() requires an object 'coin'")
}
sample(x\$sides, size = times, replace = TRUE, prob = prob)
}

# ok
toss(coin1)

toss(c('tic', 'tac', 'toe'))
#> Error in toss(c("tic", "tac", "toe")):
#> toss() requires an object 'coin'``````

A more formal strategy, and one that follows OOP principles, is to create a toss method. In R, many functions are actually methods: e.g. `print()`, `summary()`, `plot()`, `str()`, etc.

``````# print method
print
#> function (x, ...)
#> UseMethod("print")
#> <bytecode: 0x7fb08aa78908>
#> <environment: namespace:base>``````

These types of functions are not really one unique function, they typically comprise a collection or family of functions for printing objects, computing summaries, plotting, etc. Depending on the class of the object, a generic method will look for a specific function for that class:

``````# methods for objects "matrix"
methods(class = "matrix")
#>  [1] anyDuplicated as.data.frame as.raster     boxplot       determinant
#>  [6] duplicated    edit          head          isSymmetric   relist
#> [11] subset        summary       tail          unique
#> see '?methods' for accessing help and source code``````

4.3 Generic Method `toss`

When implementing new methods, you begin by creating a generic method with the function `UseMethod()`:

``````# generic method 'toss'
toss <- function(x, ...) UseMethod("toss")``````

The function `UseMethod()` allows you to declare the name of a method. In this example we are telling R that the function `toss()` is now a generic `"toss"` method. Note the use of `"..."` in the function definition, this will allow you to include more arguments when you define specific methods based on `"toss"`.

A generic method alone is not very useful. You need to create specific cases for the generic. In our example, we only have one class `"coin"`, so that is the only class we will allow `toss` to be applied on. The way to do this is by defining `toss.coin()`:

``````# specific method 'toss' for objects "coin"
toss.coin <- function(x, times = 1, prob = NULL) {
sample(x\$sides, size = times, replace = TRUE, prob = prob)
}``````

The name of the method, `"toss"`, comes first, followed by a dot `"."`, followed by the name of the class, `"coin"`. Notice that the body of the function `toss.coin()` does not include the `stop()` command anymore.

To use the `toss()` method on a `"coin"` object, you don’t really have to call `toss.coin()`; calling `toss()` is enough:

``````toss(coin1)
#> [1] "tails"``````

How does `toss()` work? Becasue `toss()` is now a generic method, everytime you use it, R will look at the class of the input, and see if there is an associated `"toss"` method. In the previous example, `coin1` is an object of class `"coin"`, for which there is a specific `toss.coin()` method. Thus using `toss()` on a `"coin"` object works fine.

Now let’s try `toss()` on the character vector `c('tic', 'tac', 'toe')`:

``````# no toss() method for regular vectors
toss(c('tic', 'tac', 'toe'))
#> Error in UseMethod("toss"): no applicable method for 'toss' applied to an object of class "character"``````

When you try to use `toss()` on an object that is not of class `"coin"`, you get a nice error message.

Because an object `"coin"` already contains an element `prob`, the `toss.coin()` function does not really an argument `prob`. Instead, we can pass this value from the coin object. Here’s a new definition of `toss.coin()`:

``````toss.coin <- function(x, times = 1) {
sample(x\$sides, size = times, replace = TRUE, prob = x\$prob)
}``````

``````set.seed(2341)