What does the dot mean in R – personal preference, naming convention or more?

I am (probably) NOT referring to the "all other variables" meaning like var1~. here. I was pointed to plyr once again and looked into mlplyand wondered why parameters are defined with leading dot like this:

function (.data, .fun = NULL, ..., .expand = TRUE, .progress = "none", 
.parallel = FALSE) 
{
if (is.matrix(.data) & !is.list(.data)) 
    .data <- .matrix_to_df(.data)
f <- splat(.fun)
alply(.data = .data, .margins = 1, .fun = f, ..., .expand = .expand, 
    .progress = .progress, .parallel = .parallel)
}
<environment: namespace:plyr>

What's the use of that? Is it just personal preference, naming convention or more? Often R is so functional that I miss a trick that's long been done before.


Solution 1:

A dot in function name can mean any of the following:

  • nothing at all
  • a separator between method and class in S3 methods
  • to hide the function name

Possible meanings

1. Nothing at all

The dot in data.frame doesn't separate data from frame, other than visually.

2. Separation of methods and classes in S3 methods

plot is one example of a generic S3 method. Thus plot.lm and plot.glm are the underlying function definitions that are used when calling plot(lm(...)) or plot(glm(...))

3. To hide internal functions

When writing packages, it is sometimes useful to use leading dots in function names because these functions are somewhat hidden from general view. Functions that are meant to be purely internal to a package sometimes use this.

In this context, "somewhat hidden" simply means that the variable (or function) won't normally show up when you list object with ls(). To force ls to show these variables, use ls(all.names=TRUE). By using a dot as first letter of a variable, you change the scope of the variable itself. For example:

x <- 3
.x <- 4

ls()
[1] "x"

ls(all.names=TRUE)
[1] ".x" "x" 

x
[1] 3
.x
[1] 4

4. Other possible reasons

In Hadley's plyr package, he uses the convention to use leading dots in function names. This as a mechanism to try and ensure that when resolving variable names, the values resolve to the user variables rather than internal function variables.


Complications

This mishmash of different uses can lead to very confusing situations, because these different uses can all get mixed up in the same function name.

For example, to convert a data.frame to a list you use as.list(..)

as.list(iris)

In this case as.list is a S3 generic method, and you are passing a data.frame to it. Thus the S3 function is called as.list.data.frame:

> as.list.data.frame
function (x, ...) 
{
    x <- unclass(x)
    attr(x, "row.names") <- NULL
    x
}
<environment: namespace:base>

And for something truly spectacular, load the data.table package and look at the function as.data.table.data.frame:

> library(data.table)

> methods(as.data.table)
[1] as.data.table.data.frame* as.data.table.data.table* as.data.table.matrix*    

   Non-visible functions are asterisked


> data.table:::as.data.table.data.frame
function (x, keep.rownames = FALSE) 
{
    if (keep.rownames) 
        return(data.table(rn = rownames(x), x, keep.rownames = FALSE))
    attr(x, "row.names") = .set_row_names(nrow(x))
    class(x) = c("data.table", "data.frame")
    x
}
<environment: namespace:data.table>

Solution 2:

At the start of a name it works like the UNIX filename convention to keep objects hidden by default.

ls()
character(0)

.a <- 1

ls()
character(0)

ls(all.names = TRUE)
[1] ".a"

It can be just a token with no special meaning, it's not doing anything more than any other allowed token.

my.var <- 1
my_var <- 1
myVar <- 1

It's used for S3 method dispatch. So, if I define simple class "myClass" and create objects with that class attribute, then generic functions such as print() will automatically dispatch to my specific print method.

myvar <- 1

print(myvar)

class(myvar) <- c("myClass", class(myvar))

print.myClass <- function(x, ...) {

    print(paste("a special message for myClass objects, this one has length", length(x)))
    return(invisible(NULL))
}

print(myvar)

There is an ambiguity in the syntax for S3, since you cannot tell from a function's name whether it is an S3 method or just a dot in the name. But, it's a very simple mechanism that is very powerful.

There's a lot more to each of these three aspects, and you should not take my examples as good practice, but they are the basic differences.