# Good
AddRow()
Permute()
# Bad
RowAdder()
Permutation()3 Functions
3.1 Naming
As well as following the general advice for object names in Section 2.1, strive to use verbs for function names:
3.2 Anonymous functions
Use the new lambda syntax: \(x) x + 1 when writing short anonymous functions (i.e. when you define a function in an argument without giving it an explicit name).
# Good
map(xs, \(x) mean((x + 5)^2))
map(xs, function(x) mean((x + 5)^2))
# Bad
map(xs, ~ mean((.x + 5)^2))Don’t use \() for multi-line functions:
# Good
map(xs, function(x) {
mean((x + 5)^2)
})
# Bad
map(xs, \(x) {
mean((x + 5)^2)
})Or when creating named functions:
# Good
cv <- function(x) {
sd(x) / mean(x)
}
# Bad
cv <- \(x) sd(x) / mean(x)Avoid using \() in a pipe, and remember to use informative argument names.
3.3 Multi-line function definitions
There are two options if the function name and definition can’t fit on a single line. In both cases, each argument goes on its own line; the difference is how deep you indent it and where you put ) and {:
Single-indent: indent the argument name with a single indent (i.e. two spaces). The trailing
)and leading{go on a new line.# Good LongFunctionName <- function( a = "a long argument", b = "another argument", c = "another long argument" ) { # As usual code is indented by two spaces. }Hanging-indent: indent the argument name to match the opening
(offunction. The trailing)and leading{go on the same line as the last argument.# Good LongFunctionName <- function(a = "a long argument", b = "another argument", c = "another long argument") { # As usual code is indented by two spaces. }
These styles are designed to clearly separate the function definition from its body.
# Bad
LongFunctionName <- function(a = "a long argument",
b = "another argument",
c = "another long argument") {
# Here it's hard to spot where the definition ends and the
# code begins, and to see all three function arguments
}If a function argument can’t fit on a single line, this is a sign you should rework the argument to keep it short and sweet.
3.4 return()
Do not rely on R’s implicit return feature. It is better to be clear about your intent to return() an object (ref).
# Good
FindAbs <- function(x) {
if (x > 0) {
return(x)
}
return(x * -1)
}
AddTwo <- function(x, y) {
return(x + y)
}
# Bad
AddTwo <- function(x, y) {
x + y
}Return statements should always be on their own line because they have important effects on the control flow. See also control flow modifiers.
# Good
FindAbs <- function(x) {
if (x > 0) {
return(x)
}
return(x * -1)
}
# Bad
FindAbs <- function(x) {
if (x > 0) return(x)
return(x * -1)
}If your function is called primarily for its side-effects (like printing, plotting, or saving to disk), it should return the first argument invisibly. This makes it possible to use the function as part of a pipe. print methods should usually do this, like this example from httr:
PrintURL <- function(x, ...) {
cat("Url: ", build_url(x), "\n", sep = "")
invisible(x)
}
3.5 Comments
In code, use comments to explain the “why” not the “what” or “how”. Each line of a comment should begin with the comment symbol and a single space:
#.Comments should be in sentence case, and only end with a full stop if they contain at least two sentences: