3 Functions

3.1 Naming

As well as following the general advice for object names, strive to use verbs for function names:

# Good
add_row()
permute()

# Bad
row_adder()
permutation()

3.2 Long lines

There are two options if the function name and definition can’t fit on a single line:

  • Function-indent: place each argument on its own line, and indent to match the opening ( of function:

    long_function_name <- function(a = "a long argument",
                                   b = "another argument",
                                   c = "another long argument") {
      # As usual code is indented by two spaces.
    }
  • Double-indent: Place each argument of its own double indented line.

    long_function_name <- function(
        a = "a long argument",
        b = "another argument",
        c = "another long argument") {
      # As usual code is indented by two spaces.
    }

In both cases the trailing ) and leading { should go on the same line as the last argument.

Prefer function-indent style to double-indent style when it fits.

These styles are designed to clearly separate the function definition from its body.

# Bad
long_function_name <- 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.3 return()

Only use return() for early returns. Otherwise, rely on R to return the result of the last evaluated expression.

# Good
find_abs <- function(x) {
  if (x > 0) {
    return(x)
  }
  x * -1
}
add_two <- function(x, y) {
  x + y
}

# Bad
add_two <- function(x, y) {
  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
find_abs <- function(x) {
  if (x > 0) {
    return(x)
  }
  x * -1
}

# Bad
find_abs <- function(x) {
  if (x > 0) return(x)
  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:

print.url <- function(x, ...) {
  cat("Url: ", build_url(x), "\n", sep = "")
  invisible(x)
}

3.4 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: #.

# Good

# Objects like data frames are treated as leaves
x <- map_if(x, is_bare_list, recurse)


# Bad

# Recurse only with bare lists
x <- map_if(x, is_bare_list, recurse)

Comments should be in sentence case, and only end with a full stop if they contain at least two sentences:

# Good

# Objects like data frames are treated as leaves
x <- map_if(x, is_bare_list, recurse)

# Do not use `is.list()`. Objects like data frames must be treated
# as leaves.
x <- map_if(x, is_bare_list, recurse)


# Bad

# objects like data frames are treated as leaves
x <- map_if(x, is_bare_list, recurse)

# Objects like data frames are treated as leaves.
x <- map_if(x, is_bare_list, recurse)