Active Bindings in R: makeActiveBinding() for Computed Variables
An active binding is a variable that runs a function every time you read or write to it. Instead of storing a static value, it computes a result on access — like a property getter in object-oriented languages. Create them with makeActiveBinding().
What Are Active Bindings?
Normal variables store a value. Active bindings run a function:
# Normal variable: stores a value
x <- 42
cat("x:", x, "\n") # Just returns 42
# Active binding: runs a function each time you access it
makeActiveBinding("now", function() Sys.time(), globalenv())
cat("now:", format(now), "\n")
Sys.sleep(0.1)
cat("now:", format(now), "\n") # Different value each time!
# Clean up
rm(now)
If the function accepts an argument, you can set the binding's value:
env <- new.env(parent = emptyenv())
env$.temp_celsius <- 0
makeActiveBinding("temp_fahrenheit", function(value) {
if (missing(value)) {
# Getter: convert C to F
env$.temp_celsius * 9/5 + 32
} else {
# Setter: convert F to C and store
env$.temp_celsius <- (value - 32) * 5/9
}
}, globalenv())
env$.temp_celsius <- 100
cat("100C in F:", temp_fahrenheit, "\n")
temp_fahrenheit <- 32
cat("32F in C:", env$.temp_celsius, "\n")
rm(temp_fahrenheit)
Practical Uses
Validated Assignment
env <- new.env(parent = emptyenv())
env$.age <- 25
makeActiveBinding("age", function(value) {
if (missing(value)) return(env$.age)
if (!is.numeric(value) || value < 0 || value > 150) {
stop("age must be a number between 0 and 150")
}
env$.age <- value
}, globalenv())
cat("Age:", age, "\n")
age <- 30
cat("Updated age:", age, "\n")
# This would error:
tryCatch(
age <- -5,
error = function(e) cat("Error:", conditionMessage(e), "\n")
)
cat("Age after invalid assignment:", age, "\n") # Still 30
rm(age)
# Exercise: Create an active binding called "random_greeting"
# that returns a random greeting each time it's accessed.
# Greetings: "Hello!", "Hi there!", "Hey!", "Greetings!", "Howdy!"
# Write your code below:
**Explanation:** Each time `random_greeting` is accessed, the function runs and `sample()` picks a random element. No two accesses are guaranteed to return the same value.
Summary
Feature
Syntax
Purpose
Create binding
makeActiveBinding("name", fn, env)
Define computed variable
Read-only
function() { ... } (no args)
Computed property
Read-write
function(value) { ... }
Validated getter/setter
Check if active
bindingIsActive("name", env)
Test binding type
R6 active fields
active = list(name = function() ...)
OOP computed properties
FAQ
How are active bindings used in R6 classes?
R6 classes have an active field in their class definition. Active fields look like regular fields to the user but run functions underneath — perfect for computed properties, validation, and lazy initialization.
Do active bindings have performance overhead?
Yes — each access runs a function call instead of a simple value lookup. For rarely-accessed properties this is negligible. For hot loops accessing millions of times, use regular variables instead.
What's Next?
R6 Classes — active bindings are a key feature of R6's OOP system
R Environments — active bindings live in environments
R Closures — the function in an active binding is often a closure