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

3

96

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.

Damp answered 23/9, 2011 at 8:51 Comment(4)
Good question. Also the use of dots in function names (is.na, as.data.frame, ...) is not usual in other programing languages, but I like it.Celtic
Further explanation: stats.stackexchange.com/questions/10712/…Mccaslin
The purrr package (purrr.tidyverse.org) now adds another layer of meaning as in ~ .x+1 == function(x) x+1.Weksler
So much of this confusion could be solved if keyboards put the _ key in an reasily reachable spot (like, you know, instead of a Shopping key…)Weksler
T
137

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 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>
Trifling answered 23/9, 2011 at 9:31 Comment(8)
What do you mean with "somewhat hidden"? The dot doesn't change the variable scope, does it? So its hidden only in the sense that users normally don't use dot at the beginning of variable name, right?Celtic
@TomasT. I have edited my answer. Yes, adding a dot changes scope, so x and .x are different variables. Dotted variables are hidden because they don't show up in ls unless you use ls(all.names=TRUE)Trifling
but that's not what is usualy meat with "scope". Scope is usualy used in terms of global / local (to function, package, ...), not just being hidden in ls() (but still accessible).Celtic
Oh, I see what you mean. No, the scope behaviour for x and .x are the same. They just happen to be different variables.Trifling
I only do that in plyr, because you want to distinguish between arguments to the plyr function and arguments to the function that plyr is calling.Grooms
plyr also has a . function. get(".") ; function (..., .env = parent.frame()) { structure(as.list(match.call()[-1]), env = .env, class = "quoted") }Suber
Also, in the context of a function call with "...", you may encounter two dots, as in foo = function(...) print(..2) ; foo("a", "b", "c").Suber
This is really confusing, why didn't they stick to one behaviour, it would have made things much more understandable...Deery
R
31

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.

Reconstructionist answered 23/9, 2011 at 9:15 Comment(2)
Why do you wanna hide some object that is not global anyway?Damp
You can also define variable or function as a single dot! Like .=1 or .(1). That's really weird, especially in expressions .*.+. :-)Celtic
T
0

If a user defines a function .doSomething and is lazy to specify all the roxygen documentation for parameters, it will not generate errors for compiling the package

Twohanded answered 4/11, 2019 at 15:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.