4.11: Generic Functions
There’s one really important thing that I omitted when I discussed functions earlier on in Section 3.5, and that’s the concept of a
generic function
. The two most notable examples that you’ll see in the next few chapters are
summary()
and
plot()
, although you’ve already seen an example of one working behind the scenes, and that’s the
print()
function. The thing that makes generics different from the other functions is that their behaviour changes, often quite dramatically, depending on the
class()
of the input you give it. The easiest way to explain the concept is with an example. With that in mind, lets take a closer look at what the
print()
function actually does. I’ll do this by creating a formula, and printing it out in a few different ways. First, let’s stick with what we know:
my.formula <- blah ~ blah.blah # create a variable of class "formula"
print( my.formula ) # print it out using the generic print() function
## blah ~ blah.blah
So far, there’s nothing very surprising here. But there’s actually a lot going on behind the scenes here. When I type
print( my.formula )
, what actually happens is the
print()
function checks the class of the
my.formula
variable. When the function discovers that the variable it’s been given is a formula, it goes looking for a function called
print.formula()
, and then delegates the whole business of printing out the variable to the
print.formula()
function.
63
For what it’s worth, the name for a “dedicated” function like
print.formula()
that exists only to be a special case of a generic function like
print()
is a
method
, and the name for the process in which the generic function passes off all the hard work onto a method is called
method dispatch
. You won’t need to understand the details at all for this book, but you do need to know the gist of it; if only because a lot of the functions we’ll use are actually generics. Anyway, to help expose a little more of the workings to you, let’s bypass the
print()
function entirely and call the formula method directly:
print.formula( my.formula ) # print it out using the print.formula() method
## Appears to be deprecated
There’s no difference in the output at all. But this shouldn’t surprise you because it was actually the
print.formula()
method that was doing all the hard work in the first place. The
print()
function itself is a lazy bastard that doesn’t do anything other than select which of the methods is going to do the actual printing.
Okay, fair enough, but you might be wondering what would have happened if
print.formula()
didn’t exist? That is, what happens if there isn’t a specific method defined for the class of variable that you’re using? In that case, the generic function passes off the hard work to a “default” method, whose name in this case would be
print.default()
. Let’s see what happens if we bypass the
print()
formula, and try to print out
my.formula
using the
print.default()
function:
print.default( my.formula ) # print it out using the print.default() method
## blah ~ blah.blah
## attr(,"class")
## [1] "formula"
## attr(,".Environment")
## <environment: R_GlobalEnv>
Hm. You can kind of see that it is trying to print out the same formula, but there’s a bunch of ugly low-level details that have also turned up on screen. This is because the
print.default()
method doesn’t know anything about formulas, and doesn’t know that it’s supposed to be hiding the obnoxious internal gibberish that R produces sometimes.
At this stage, this is about as much as we need to know about generic functions and their methods. In fact, you can get through the entire book without learning any more about them than this, so it’s probably a good idea to end this discussion here.