R Namespaces: How Packages Export Functions & Prevent Conflicts
A namespace is the mechanism that lets R packages have internal (private) functions and public (exported) functions. It prevents packages from clashing when they define functions with the same name, and it ensures packages always find the right version of a function.
Without namespaces, every function from every loaded package would compete in one flat namespace. Two packages defining filter() would be chaos. Namespaces solve this by giving each package its own isolated environment.
# :: accesses exported functions (safe, documented)
cat("stats::median(1:10) =", stats::median(1:10), "\n")
cat("base::sum(1:10) =", base::sum(1:10), "\n")
# ::: accesses internal functions (use with caution)
# These are not part of the public API and may change
cat("\nInternal function example:\n")
cat("stats:::Pillai exists:", exists("Pillai", envir = asNamespace("stats")), "\n")
When to Use ::
# Best practice: use :: for clarity and avoiding conflicts
# Instead of loading the whole package:
# library(stats)
# result <- filter(data, ...) # Which filter? stats or dplyr?
# Use :: to be explicit:
x <- 1:20
smoothed <- stats::filter(x, rep(1/5, 5))
cat("Smoothed (middle values):", round(smoothed[3:8], 2), "\n")
Imports and Exports
A package's NAMESPACE file declares:
Exports: functions available to users via library() or ::
Imports: functions from other packages that this package uses internally
# See what a package exports
exports <- getNamespaceExports("stats")
cat("stats exports", length(exports), "functions\n")
cat("Sample:", paste(sample(exports, 8), collapse = ", "), "\n")
# See what a package imports
imports <- getNamespaceImports("stats")
cat("\nstats imports from", length(imports), "packages\n")
cat("Packages:", paste(names(imports), collapse = ", "), "\n")
The import mechanism is why packages work reliably: when ggplot2 calls a function internally, it finds the imported version in its namespace — not whatever version happens to be loaded in the user's session.
Namespace Resolution in Action
# Demonstrate: package functions find OTHER package functions
# through the namespace, not through the search path
# Even if you mask a base function, packages still work:
cat("Before: base::sum exists\n")
# This doesn't break packages that use sum internally:
# sum <- function(...) "I'm a fake sum"
# stats::var() internally calls sum() — it finds base::sum
# through its namespace imports, not through the search path
cat("stats::var(1:10) =", stats::var(1:10), "\n")
cat("Namespace ensures packages find the right functions\n")
Practice Exercise
# Exercise: For the "base" package, find:
# 1. How many functions are exported
# 2. How many total objects are in the namespace
# 3. Name 3 functions that are in the namespace but NOT exported
# Write your code below:
**Explanation:** `getNamespaceExports()` lists what's public. `ls(asNamespace())` lists everything. The difference gives you internal-only objects that package authors use but don't expose to users.
Summary
Concept
Description
Access
Package env
Exported functions only
as.environment("package:stats")
Namespace env
All functions (public + private)
asNamespace("stats")
:: operator
Access exported function
stats::median()
::: operator
Access internal function
stats:::internal_fn()
Exports
What users can see
getNamespaceExports("pkg")
Imports
What the package uses internally
getNamespaceImports("pkg")
FAQ
Why shouldn't I use ::: in production code?
Functions accessed via ::: are not part of the package's public API. The package author can change, rename, or remove them at any time without warning. Your code could break silently after a package update.
How do I resolve conflicts when two packages export the same function name?
Three options: (1) Use package::function() to be explicit. (2) Load the package you want to use last — it masks the earlier one. (3) Use conflicted::conflict_prefer("filter", "dplyr") to declare your preference.
What's Next?
R Environments — the foundation namespaces are built on
R Internal Functions — .Internal(), .Call(), and R's C interface
Lexical Scoping — how R uses environment chains for variable lookup