# Good
add_row()
permute()
# Bad
row_adder()
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
<- function(x) {
cv sd(x) / mean(x)
}
# Bad
<- \(x) sd(x) / mean(x) cv
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 <- function( long_function_name 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 <- function(a = "a long argument", long_function_name 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
<- function(a = "a long argument",
long_function_name 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 S7
In S7, the method definition can be long because the function name is replaced by a method call that specifies the generic and dispatch classes. In this case we recommend the single-indent style.
method(from_provider, list(openai_provider, class_any)) <- function(
provider,
x,
...,error_call = caller_env()
) {
... }
If the method definition is too long to fit on one line, use the usual rules to spread the method arguments across multiple lines:
method(
from_provider,list(openai_provider, class_any, a_very_long_class_name)
<- function(
)
provider,
x,
...,error_call = caller_env()
) {
... }
3.5 return()
Only use return()
for early returns. Otherwise, rely on R to return the result of the last evaluated expression.
# Good
<- function(x) {
find_abs if (x > 0) {
return(x)
}* -1
x
}<- function(x, y) {
add_two + y
x
}
# Bad
<- function(x, y) {
add_two return(x + y)
}
Return statements should always be on their own line because they have important effects on the control flow. See also inline statements.
# Good
<- function(x) {
find_abs if (x > 0) {
return(x)
}* -1
x
}
# Bad
<- function(x) {
find_abs if (x > 0) return(x)
* -1
x }
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:
<- function(x, ...) {
print.url cat("Url: ", build_url(x), "\n", sep = "")
invisible(x)
}
3.6 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: