library(Q7)
#>
#> Attaching package: 'Q7'
#> The following object is masked from 'package:base':
#>
#> merge
Dog <- type(function(name, breed){
say <- function(greeting = "Woof!"){
cat(paste0(greeting,
" I am ", name, ", a ", breed,
".\n"))
}
})
max <- walter %>%
clone() %>%
implement({
name <- "Max"
})
max$say("Wussup Dawg!")
#> Wussup Dawg! I am Walter, a Husky.
max %>%
implement({
treats_eaten <- 0
eat_treat <- function(n = 1){
cat(paste(name, "eats", n, "treat(s).\n"))
treats_eaten <<- treats_eaten + n
}
}) %>%
implement({
is_satisfied <- function(){
treats_eaten > 5
}
})
max$eat_treat()
#> Max eats 1 treat(s).
max$is_satisfied()
#> [1] FALSE
max$eat_treat(2)
#> Max eats 2 treat(s).
max$is_satisfied()
#> [1] FALSE
max$eat_treat(3)
#> Max eats 3 treat(s).
max$is_satisfied()
#> [1] TRUE
Manager <- type(function(weekly_hours){
extend(Employee)(weekly_hours)
final[is_manager] <- TRUE
}, "Manager")
mike <- Manager(45)
hasOvertime <- feature_generic("hasOvertime")
hasOvertime.Employee <- feature({
is_overtime <- function() weekly_hours > 40
})
hasOvertime.Manager <- feature({
is_overtime <- function() FALSE
})
hasOvertime.Boss <- feature({
final[is_overtime] <- function(){
FALSE
}
})
Boss <-
type({
extend(Employee)(24 * 7)
},
"Boss") %>%
hasOvertime()
#> Error in (function (x, value, pos = -1, envir = as.environment(pos), inherits = FALSE, : cannot add bindings to a locked environment
jill <- Boss()
#> Error in Boss(): could not find function "Boss"
jill$is_overtime()
#> Error: object 'jill' not found
Circle <- type(
function(radius){},
"Circle")
Square <- type(
function(side){},
"Square")
hasArea <- feature_generic("hasArea")
hasArea.Square <-
feature({
area <- function(){
side ^ 2
}
})
hasArea.Circle <-
feature({
area <- function(){
radius^2 * pi
}
})
isLocked <- feature({
lockEnvironment(.my, bindings = TRUE)
})
TestLocked <- type(function(){
a <- 1
}) %>% isLocked()
test_locked <- TestLocked()
#> Error in (function() {: cannot add bindings to a locked environment
try(test_locked$a <- 666)
#> Error : object 'test_locked' not found
try(test_locked$b <- 666)
#> Error : object 'test_locked' not found
try({
test_locked %>%
implement({
a <- 666
})
})
#> Error in eval(expr, envir) : object 'test_locked' not found
This simple state machine guards a secret message with a password.
State <- type(
function(password, secret_message = ""){
name <- "DEFAULT"
cat("Processing Current State...\n")
print_current_state <- function(){
cat(paste("Current State:", name, "\n"))
}
})
LockedState <- State %>%
implement({
name <- "Locked"
print_current_state()
on_event <- function(event) {
if (event == password) {
cat("Access Granted.\n")
return(UnlockedState(password, secret_message))
} else {
cat("Wrong Password. Access Denied.\n")
return(.my)
}
}
})
UnlockedState <- State %>%
implement({
name <- "Unlocked"
print_current_state()
private[print_secret_message] <- function(){
cat(secret_message)
}
on_event <- function(event) {
if (event == password) {
return(LockedState(password, secret_message))
} else if (event == "show") {
print_secret_message()
return(.my)
} else{
cat("Invalid Instruction. \n")
return(.my)
}
}
})
SimpleDevice <- type(function(password, secret_message){
state <- LockedState(password, secret_message)
on_event <- function(event){
state <<- state$on_event(event)
}
})
device <- SimpleDevice(password = "xxx",
secret_message = "I love you.")
#> Processing Current State...
#> Current State: Locked
device$on_event("1234")
#> Wrong Password. Access Denied.
device$on_event("PvmpKinM4n777")
#> Wrong Password. Access Denied.
device$on_event("xxx")
#> Access Granted.
#> Processing Current State...
#> Current State: Unlocked
device$on_event("fiddlin...")
#> Invalid Instruction.
device$on_event("meddlin...")
#> Invalid Instruction.
device$on_event("show")
#> I love you.
device$on_event("xxx")
#> Processing Current State...
#> Current State: Locked
device$on_event("0000")
#> Wrong Password. Access Denied.
feature is subordinate to and dependent on type. It is encouraged to put all data members in a type definition, while feature mainly contain functions. If you feel significant need to parameterize a feature, think if it’s better to create a nested object or to formally extend a type. You can always re-define something in a feature post hoc.
This will be implemented in the future.
Word <- type(function(word){})
hasRepeat <- feature({
N_TIMES <- 2
repeat_word <- function(){
cat(rep(word, N_TIMES))
}
})
pear <- Word("pear") %>%
implement({
hasRepeat()
N_TIMES <- 5
})
pear$repeat_word()
#> pear pear pear pear pear
repeatWordNTimes <- function(word, times){
localize(Word)(word) %>%
hasRepeat() %>%
implement({
N_TIMES <- times
})
}
orange <- repeatWordNTimes("orange", 7)
orange$repeat_word()
#> orange orange orange orange orange orange orange
isIterable <- feature_generic("isIterable")
isIterable.default <- feature({
forEach <- function(fn){
Vector(sapply(elementData, fn))
}
final[is_iterable] <- TRUE
})
isLocked <- feature({
lockEnvironment(.my, bindings = TRUE)
})
Vector <- type(function(elementData){
elementData <- elementData
elementCount <- length(elementData)
add <- function(e){
unlockBinding("elementData", .my)
unlockBinding("elementCount", .my)
elementData <<- c(elementData, e)
elementCount <<- length(elementData)
lockBinding("elementData", .my)
lockBinding("elementCount", .my)
invisible(.my)
}
remove <- function(index){
unlockBinding("elementData", .my)
unlockBinding("elementCount", .my)
elementData <<- elementData[-index - 1]
elementCount <<- length(elementData)
lockBinding("elementData", .my)
lockBinding("elementCount", .my)
invisible(.my)
}
clear <- function(){
unlockBinding("elementData", .my)
unlockBinding("elementCount", .my)
elementData <<- c()
elementCount <<- length(elementData)
lockBinding("elementData", .my)
lockBinding("elementCount", .my)
invisible(.my)
}
firstElement <- function(){
elementData[1]
}
lastElement <- function(){
elementData[elementCount]
}
}, "Vector") %>%
isIterable() %>%
isLocked()
nums <- Vector(c(1,2,3))
#> Error in (function() {: cannot add bindings to a locked environment
nums$elementData
#> Error: object 'nums' not found
nums$add(4)
#> Error: object 'nums' not found
nums$elementData
#> Error: object 'nums' not found
nums$elementCount
#> Error: object 'nums' not found
nums$remove(0)
#> Error: object 'nums' not found
nums$elementData
#> Error: object 'nums' not found
nums$clear()
#> Error: object 'nums' not found
nums$elementData
#> Error: object 'nums' not found
nums$elementCount
#> Error: object 'nums' not found
nums$add(c(554, 665, 776))
#> Error: object 'nums' not found
nums$elementData
#> Error: object 'nums' not found
nums$is_iterable <- FALSE
#> Error: object 'nums' not found
plus_one <- function(x){
x + 1
}
nums2 <- nums$
forEach(plus_one)$
remove(0)$
add(c(888, 999))
#> Error: object 'nums' not found
nums2$elementData
#> Error: object 'nums2' not found
Microwave
Microwave <- type(function(){
food <- NULL
put_food <- function(food){
if (!is.null(.my$food)) {
stop("There's already food.")
} else {
.my$food <- food
}
}
check_food <- function(){
if (is.null(food)) {
cat("Empty.\n")
} else {
cat(paste("The food is", food, "\n"))
}
}
heat <- function(seconds){
cat(paste(food,
"is heated for",
paste0(seconds, "s\n")))
}
remove_food <- function(){
food <<- NULL
}
})
microwave <- Microwave()
microwave$check_food()
#> Empty.
microwave$put_food("chicken dinner")
microwave$put_food("meatballs")
#> Error in microwave$put_food("meatballs"): There's already food.
microwave$heat(30)
#> chicken dinner is heated for 30s
microwave$remove_food()
microwave$put_food("meatballs")
microwave$check_food()
#> The food is meatballs
microwave$heat(40)
#> meatballs is heated for 40s