Guarding State
Part of the Object Oriented Programming section of Coddy's R journey. Lesson 20 of 57.
A setter is the one place where a value can change, so it is the place to enforce the rules. It checks first and calls stop() before anything is changed, and the object stays valid:
make_account <- function(balance = 0) {
withdraw <- function(x) {
if (x <= 0) stop("amount must be positive", call. = FALSE)
if (x > balance) stop("insufficient funds", call. = FALSE)
balance <<- balance - x
}
list(withdraw = withdraw, balance = function() balance)
}
acc <- make_account(50)
msg <- tryCatch(acc$withdraw(80), error = function(e) conditionMessage(e))
cat(msg, "/", acc$balance())Output:
insufficient funds / 50lockBinding("rate", e) makes one variable of an environment read-only. Assigning to it afterwards is an error, while the other variables stay writable:
loan <- new.env()
loan$rate <- 0.05
loan$owed <- 1000
lockBinding("rate", loan)
loan$owed <- 900
msg <- tryCatch({ loan$rate <- 0; "changed" }, error = function(e) conditionMessage(e))
cat(msg, "\n")
cat(loan$rate, loan$owed, "\n")Output:
cannot change value of locked binding for 'rate'
0.05 900 lockEnvironment(e) stops new variables from being added. With bindings = TRUE it also locks every existing one, which makes the whole object read-only:
cfg <- new.env()
cfg$theme <- "dark"
lockEnvironment(cfg, bindings = TRUE)
for (step in c("change", "add")) {
msg <- tryCatch({
if (step == "change") cfg$theme <- "light" else cfg$size <- 12
"done"
}, error = function(e) conditionMessage(e))
cat(step, ":", msg, "\n")
}Output:
change : cannot change value of locked binding for 'theme'
add : cannot add bindings to a locked environment local() runs code in a fresh environment and returns its last value. Helpers defined inside stay hidden, which is how a file can keep a function private even though source() puts everything else into the global environment:
circle_area <- local({
square <- function(x) x * x
function(r) pi * square(r)
})
print(round(circle_area(2), 2))
print(exists("square"))Output:
[1] 12.57
[1] FALSEChallenge
EasyComplete the factory make_account(owner) in Account.R. The balance starts at 0 and a private history records every change as +50 or -20. The returned list has four functions:
deposit(x)stops withamount must be positiveunlessxis above 0;withdraw(x)stops withamount must be positiveorinsufficient funds;balance()returns the balance;history()returns the history as a character vector.
Check before changing anything and use call. = FALSE. The supplied code reads the owner, then lines dep 50 or wd 20, prints ok or error: and the message for each, tries to change a copy of the history, and prints the balance and the history.
Your code goes in Account.R. main.R holds the supplied input/output code and cannot be edited.
Try it yourself
source("Account.R")
# Supplied input/output code: keep it as it is
input <- suppressWarnings(readLines(file("stdin")))
acc <- make_account(input[1])
for (line in input[-1]) {
p <- strsplit(line, " ")[[1]]
amount <- as.numeric(p[2])
msg <- tryCatch({
if (p[1] == "dep") acc$deposit(amount) else acc$withdraw(amount)
"ok"
}, error = function(e) paste("error:", conditionMessage(e)))
cat(line, ": ", msg, "\n", sep = "")
}
h <- acc$history()
h[1] <- "tampered"
cat("balance: ", acc$balance(), "\n", sep = "")
cat("history: ", paste(acc$history(), collapse = ", "), "\n", sep = "")
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1S3 Basics
Working With FilesLists With a ClassConstructor FunctionsPrint and Format MethodsValidators and HelpersRecap - Temperatures4Encapsulation in R
Closures as ObjectsEnvironments as ObjectsAccessor FunctionsGuarding StateRecap - Parking Meter2S3 Generics and Methods
Generics and UseMethodDefault MethodsMethods for Base GenericsNextMethod BasicsRecap - Shape Areas8Reference Classes
Defining Reference ClassesMethods and Field UpdatesCopy SemanticsInheritance and callSuperRecap - Task Queue11Project: Library Management
Books and MembersBorrowing BooksPractice on your own: Online R compiler