Unit 02.06: Arguments in, one value out
The moment you copy a calculation for the third time, it should have become a function on the second.
Arguments in, one value out
A function names a calculation so it can be checked once and trusted afterwards. R returns the value of the last expression, so an explicit return() is rarely needed.
Arguments can be given by position or by name. Positional is compact and depends on you remembering the order; named is longer and cannot be got wrong. Defaults turn an argument into an option: the caller supplies it only when the usual value is not wanted.
A function is also the right place for a check. stopifnot() turns an impossible input into a loud failure at the point of the mistake, instead of a strange number that travels onward.
This block defines a rate function with a default and a guard, then calls it four ways.
# A function returns its last expression. You rarely need return().
visit_rate <- function(visits, residents, per = 1000) {
stopifnot(residents > 0)
visits / residents * per # this value is what comes back
}
cat("Positional :", round(visit_rate(412, 12400), 1), "\n")
cat("Named :", round(visit_rate(residents = 12400, visits = 412), 1), "\n")
cat("Default used:", formals(visit_rate)$per, "\n")
cat("Overridden :", round(visit_rate(412, 12400, per = 100), 2), "\n\n")
# Named arguments are order-free, which is why they are worth the typing.
# And a function that fails its check fails loudly rather than returning Inf:
outcome <- tryCatch(visit_rate(412, 0),
error = function(e) paste("stopped:", conditionMessage(e)))
cat(outcome, "\n")
cat("Without the check it would have returned:", 412 / 0 * 1000, "\n")
Positional and named calls both give 33.2, and naming the arguments lets them be written in either order. The default per is 1000; overriding it to 100 gives 3.32. The guard then earns its place: calling with zero residents stops with residents > 0 is not TRUE, whereas the unguarded arithmetic would have returned Inf — a value that flows happily into a mean, a chart and a report.
The mistake this prevents
The mistake is relying on argument order in a function with several similar arguments. Swapping two numeric arguments produces a number, not an error.
Takeaway
Write a function on the second repetition. Name arguments at the call site when there is more than one, give sensible defaults, and check preconditions inside the function so failures happen where the mistake was made.
