Writing Functions & Packages
A package is R's unit for organising and sharing reusable code — bundling well-written, documented functions so you (and others) can install and reuse them across projects.
Learn Writing Functions & Packages in our free R course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free R course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll write robust, validated functions, document them with roxygen comments, compose small functions into bigger tools, and understand the workflow that turns them into a sharable package.
What You'll Learn in This Lesson
1️⃣ Robust, Documented Functions
A production-quality function validates its inputs (failing clearly with stop() ), stays vectorized, and carries roxygen #' documentation describing its parameters and return value.
2️⃣ Composing Small Functions
Big tools are built from small ones. Each function does one job; you combine them. Returning a named list lets a function hand back several results at once.
3️⃣ From Functions to a Package
Packages are how R shares code. The usethis + devtools workflow scaffolds, documents, and checks one. And using any package is always the same two steps: install.packages() once, library() each session.
Your turn. Fill in the # TODO blank, run it, and compare with the expected output.
Mini-Challenge: A Reusable Toolkit
Write both functions from the outline, run them, and check against the example output. Two small, validated, reusable functions are the seed of any package.
Common Errors (and the fix)
- "could not find function" — the package isn't loaded. Add library(pkg) (after installing it once).
- library() on a missing package — install it first with install.packages("pkg") , then load it.
- Silent wrong answers — no input validation. Add stop() guards so bad input fails loudly.
- Docs out of date — you edited #' comments but forgot devtools::document() to rebuild help files.
Pro Tips
- 💡 Test as you build: the testthat package lets you write automated tests for your functions.
- 💡 Share via GitHub: others can install your package with devtools::install_github("user/repo") .
- 💡 One job per function: small, focused functions are easier to validate, document, and reuse.
📋 Quick Reference — Functions & Packages
Task
R Syntax
Validate input
if (!is.numeric(x)) stop("...")
Document
#' @param x ...
Install once
install.packages("pkg")
Load each session
library(pkg)
New package
usethis::create_package()
Build docs
devtools::document()
Frequently Asked Questions
🎉 Lesson Complete!
- ✅ Validate inputs and fail loudly with stop()
- ✅ Document functions with roxygen #' comments
- ✅ Compose small functions into bigger tools
- ✅ install.packages() once, library() each session
- ✅ usethis + devtools build a sharable package
- ✅ Next lesson: Capstone — a full data analysis report
Practice quiz
Which function makes a function fail loudly on bad input?
- warn()
- stop()
- print()
- cat()
Answer: stop(). stop() halts execution with a clear error message for invalid input.
What does install.packages("dplyr") do?
- Loads it into the session
- Downloads and installs the package once
- Documents it
- Deletes it
Answer: Downloads and installs the package once. install.packages() downloads and installs a package; you do it once.
What does library(dplyr) do?
- Installs the package
- Uninstalls it
- Loads an installed package into the session
- Publishes it to CRAN
Answer: Loads an installed package into the session. library() loads an already-installed package each session so its functions are available.
What are the #' comment lines above a function for?
- They are ignored entirely
- They speed up the function
- They are normal comments
- roxygen documentation
Answer: roxygen documentation. #' lines are roxygen2 comments that generate the function's help page.
Which function scaffolds a new package skeleton?
- usethis::create_package()
- make_package()
- new.package()
- devtools::build()
Answer: usethis::create_package(). usethis::create_package() sets up the package folder structure.
Which command builds help files from #' comments?
- library()
- devtools::document()
- install.packages()
- use_r()
Answer: devtools::document(). devtools::document() reads roxygen comments and generates the help files.
What does the @param tag describe in a roxygen comment?
- The return value
- The package name
- A function argument
- The author
Answer: A function argument. @param documents one of the function's arguments.
What causes 'could not find function' for a package's function?
- The package isn't loaded with library()
- Too many arguments
- A typo in numbers
- The function returns NULL
Answer: The package isn't loaded with library(). You must load the installed package with library(pkg) before using its functions.
Why return a named list from a function?
- Lists are required
- To bundle several results in one object
- To make it run faster
- Lists print prettier
Answer: To bundle several results in one object. A named list lets a function hand back several results at once.
How can others install your package straight from GitHub?
- library(github)
- install.packages(github)
- use_github()
- devtools::install_github("user/repo")
Answer: devtools::install_github("user/repo"). devtools::install_github() installs a package directly from a GitHub repo.
Continue this course
- Previous: Linear Models (lm)
- Next: Capstone: A Data Analysis Report