Function argument naming conventions (`.x` vs `x`)

Actually, this turns out not to be true. Hadley gave the answer as a reply to the OP's GitHub issue when he closed it:

"It is not a general principle. It's something that only apply to FP tools like purrr, because you don't want to confuse the arguments to map() with the arguments to .f" [sic]

That explains why, for instance, count() uses x and not .x.

... is not specific to functional programming.

Thank you for your post @torvaney: I think we all learned from it and now we know where to use the dot and where not to use it :slight_smile:

2 Likes

Hmm i stand corrected then... :wink:

Thinking about it, it would be overkill to use the . convention for all functions that take ...; however, It does make some sense for all functions that take ... in an nse context (see dplyr::mutate() that also uses .data and not data). It makes less sense for functions that just pass on arguments (like print()).

I personally would use .x were I to write a function like dplyr::count().

I think we can even make it a bit more specific than that. When ... can take any keyword like mutate or map (and therefore clash with any of the arguments), prefixing argument names with . is useful. In general, the added dot might make the argument names less intuitive and therefore would not be encouraged in all cases.

Does this seem like a fair conclusion to the above discussion?

I think that Hadley's statement is pretty clear and sums up the situation (by the way, maybe you should update your voted answer so that people not reading the full thread don't learn something incorrect by taking the current solution as a correct answer).

Thank you for adding Hadley's answer at the bottom of your post @torvaney! I was thinking that it might be a good idea to untick the selected answer as well to make sure that future readers don't learn something that turned out not to be correct. Thank you!

Digging this up after a few months, just to say that the dot prefix is "necessary" in functions using a ... argument only if the arguments fed to ... are named, there is no risk of conflict for count for example.

2 Likes

Ah! Great! That makes sense. Thank you! :slight_smile:

I should note one oddity I found: If you are using environment() to inspect/get at the function arguments, argument names that start with a dot are not captured, as far as I can tell. And I don't know a way around that:

test <- function( x = 1) {
    print(as.list(environment()))
}
test()
#> $x
#> [1] 1

test <- function( .x = 1) {
    print(as.list(environment()))
}
test()
#> list()

Created on 2019-03-28 by the reprex package (v0.2.1)

They are hidden. For example, with ls() you need to set all.names = TRUE to see names that start with .. You can find more info about how environments work here - 7 Environments | Advanced R

test <- function( x = 1) {
  print(rlang::env_print())
}
test()
#> <environment: 0x561c1778aaa0>
#> parent: <environment: global>
#> bindings:
#>  * x: <lazy>
#> <environment: 0x561c1778aaa0>

test <- function( .x = 1) {
  print(rlang::env_print())
}
test()
#> <environment: 0x561c168abed0>
#> parent: <environment: global>
#> bindings:
#>  * .x: <lazy>
#> <environment: 0x561c168abed0>

Created on 2019-03-28 by the reprex package (v0.2.1)

1 Like