OK, I think I got the "how to make handlers 'dynamical'" figured out now.
The trick is to use sys.frame()
if you want to "dynamically" change values in the frame where the original expression (argument expr
) of withCallingHandlers()
is evaluated.
To see what's going on, you can call sys.calls()
and sys.call()
inside your handler.
Note that it is not necessary to actually re-evaluate the original expression inside the handler after you made your changes! withCallingHandlers()
takes care of that automatically (i.e. we could have omitted value <- eval(sys.call(-pos))
in the example below).
Example
Default condition constructor:
condition <- function(subclass, message, call=sys.call(-1), ...) {
structure(
class=c(subclass, "condition"),
list(message=message, call=call),
...
)
}
Custom condition signaler:
custom_triggerCondition <- function(subclass, message, call=sys.call(-1), ...) {
cond <- condition(subclass, message, call=call, ...)
signalCondition(cond)
}
Example function in which custom signaler is used:
myLog <- function(x) {
if (is.numeric(x) && x == 5) {
custom_triggerCondition(subclass="MyTriggerCondition",
message="This is an example alternative condition"
)
}
log(x)
}
myLog(2)
[1] 0.6931472
myLog(5)
[1] 1.609438
Note how MyTriggerCondition
differs from a message
: unless we provide a handler, nothing happens and/or is signaled.
Let's see how things look when the conditions for signaling MyTriggerCondition
are not met:
## No condition signaled //
res <- withCallingHandlers(
{
myLog(x=2)
},
## Handler for message condition class 'MyTriggerCondition' //
MyTriggerCondition=function(cond) {
## Omitted for the sake of compactness.
## See next call to 'withCallingHandlers()' for actual code
return(NULL)
}
)
This gives us:
res
[1] 0.6931472
Let's see how things look when the conditions for signaling MyTriggerCondition
are met:
## Condition 'MyTriggerCondition' signaled //
res <- withCallingHandlers(
{
myLog(x=5)
},
## Handler for message condition class 'MyTriggerCondition' //
MyTriggerCondition=function(cond) {
message("handler> System calls:")
syscalls <- sys.calls()
print(syscalls)
message("-------------------------------------------------------------")
message("handler> This is what happened:")
message(conditionMessage(cond))
message("handler> Condition class:")
print(class(cond))
message("handler> Now I'm handling the condition by changing 'x' to 10:")
pos <- length(syscalls) - 2
message(c(
paste0("handler> Need to go back '", pos, "'\n"),
"handler> frames for the desired system call:")
)
message(paste0("handler> Getting system call [-", pos, "]:"))
syscall <- sys.call(-pos)
print(syscall)
message(paste0("handler> Evaluating system call [-", pos, "]:"))
value <- eval(sys.call(-pos))
message("handler> Evaluation result:")
print(value)
message(paste0("handler> Getting system frame [-", pos, "]:"))
sysframe <- sys.frame(-pos)
print(sysframe)
message(paste0("handler> Listing content of system frame [-", pos, "]:"))
print(ls(sysframe))
message(paste0("handler> Getting old value of 'x' in system frame [-", pos, "]:"))
print(sysframe$x)
message(paste0("handler> Overwriting content of system frame [-", pos, "]:"))
message("handler> 'x' is set to 10")
sysframe$x <- 10
message(paste0("handler> Inspect overwriting result in system frame [-", pos, "]:"))
print(sysframe$x)
message(paste0("handler> Re-evaluating system call [-", pos, "] (locally):"))
value <- eval(sys.call(-pos))
message("handler> Re-evaluation result (locally):")
print(value)
message(c(
"handler> IMPORTANT:\n",
"handler> Note how the expression evaluated is still\n",
paste0("handler> myLog(x=5): ", value, "when re-evaluated 'locally'\n"),
"handler> inside the handler function.\n",
"handler> But also note that the **actual** return value\n",
"handler> of the handler or 'withCallingHandlers()'\n",
"handler> will be myLog(x=10): 2.302585 as the handler\n",
"handler> changed the value of 'x'!"
)
)
return(value)
}
)
Let's run this:
handler> System calls:
[[1]]
withCallingHandlers({
myLog(x = 5)
}, MyTriggerCondition = function(cond) {
message("handler> System calls:")
syscalls <- sys.calls()
print(syscalls)
message("-------------------------------------------------------------")
message("handler> This is what happened:")
message(conditionMessage(cond))
message("handler> Condition class:")
print(class(cond))
message("handler> Now I'm handling the condition by changing 'x' to 10:")
pos <- length(syscalls) - 2
message(c(paste0("handler> Need to go back '", pos, "'\n"),
"handler> frames for the desired system call:"))
message(paste0("handler> Getting system call [-", pos, "]:"))
syscall <- sys.call(-pos)
print(syscall)
message(paste0("handler> Evaluating system call [-", pos,
"]:"))
value <- eval(sys.call(-pos))
message("handler> Evaluation result:")
print(value)
message(paste0("handler> Getting system frame [-", pos, "]:"))
sysframe <- sys.frame(-pos)
print(sysframe)
message(paste0("handler> Listing content of system frame [-",
pos, "]:"))
print(ls(sysframe))
message(paste0("handler> Getting old value of 'x' in system frame [-",
pos, "]:"))
print(sysframe$x)
message(paste0("handler> Overwriting content of system frame [-",
pos, "]:"))
message("handler> 'x' is set to 10")
sysframe$x <- 10
message(paste0("handler> Inspect overwriting result in system frame [-",
pos, "]:"))
print(sysframe$x)
message(paste0("handler> Re-evaluating system call [-", pos,
"] (locally):"))
value <- eval(sys.call(-pos))
message("handler> Re-evaluation result (locally):")
print(value)
message(c("handler> IMPORTANT:\n", "handler> Note how the expression evaluated is still\n",
paste0("handler> myLog(x=5): ", value, "when re-evaluated 'locally'\n"),
"handler> inside the handler function.\n", "handler> But also note that the **actual** return value\n",
"handler> of the handler or 'withCallingHandlers()'\n",
"handler> will be myLog(x=10): 2.302585 as the handler\n",
"handler> changed the value of 'x'!"))
return(value)
})
[[2]]
myLog(x = 5)
[[3]]
custom_triggerCondition(subclass = "MyTriggerCondition", message = "This is an example alternative condition")
[[4]]
signalCondition(cond)
[[5]]
(function (cond)
{
message("handler> System calls:")
syscalls <- sys.calls()
print(syscalls)
message("-------------------------------------------------------------")
message("handler> This is what happened:")
message(conditionMessage(cond))
message("handler> Condition class:")
print(class(cond))
message("handler> Now I'm handling the condition by changing 'x' to 10:")
pos <- length(syscalls) - 2
message(c(paste0("handler> Need to go back '", pos, "'\n"),
"handler> frames for the desired system call:"))
message(paste0("handler> Getting system call [-", pos, "]:"))
syscall <- sys.call(-pos)
print(syscall)
message(paste0("handler> Evaluating system call [-", pos,
"]:"))
value <- eval(sys.call(-pos))
message("handler> Evaluation result:")
print(value)
message(paste0("handler> Getting system frame [-", pos, "]:"))
sysframe <- sys.frame(-pos)
print(sysframe)
message(paste0("handler> Listing content of system frame [-",
pos, "]:"))
print(ls(sysframe))
message(paste0("handler> Getting old value of 'x' in system frame [-",
pos, "]:"))
print(sysframe$x)
message(paste0("handler> Overwriting content of system frame [-",
pos, "]:"))
message("handler> 'x' is set to 10")
sysframe$x <- 10
message(paste0("handler> Inspect overwriting result in system frame [-",
pos, "]:"))
print(sysframe$x)
message(paste0("handler> Re-evaluating system call [-", pos,
"] (locally):"))
value <- eval(sys.call(-pos))
message("handler> Re-evaluation result (locally):")
print(value)
message(c("handler> IMPORTANT:\n", "handler> Note how the expression evaluated is still\n",
paste0("handler> myLog(x=5): ", value, "when re-evaluated 'locally'\n"),
"handler> inside the handler function.\n", "handler> But also note that the **actual** return value\n",
"handler> of the handler or 'withCallingHandlers()'\n",
"handler> will be myLog(x=10): 2.302585 as the handler\n",
"handler> changed the value of 'x'!"))
return(value)
})(list(message = "This is an example alternative condition",
call = myLog(x = 5)))
-------------------------------------------------------------
handler> This is what happened:
This is an example alternative condition
handler> Condition class:
[1] "MyTriggerCondition" "condition"
handler> Now I'm handling the condition by changing 'x' to 10:
handler> Need to go back '3'
handler> frames for the desired system call:
handler> Getting system call [-3]:
myLog(x = 5)
handler> Evaluating system call [-3]:
handler> Evaluation result:
[1] 1.609438
handler> Getting system frame [-3]:
<environment: 0x000000001eb17c98>
handler> Listing content of system frame [-3]:
[1] "x"
handler> Getting old value of 'x' in system frame [-3]:
[1] 5
handler> Overwriting content of system frame [-3]:
handler> 'x' is set to 10
handler> Inspect overwriting result in system frame [-3]:
[1] 10
handler> Re-evaluating system call [-3] (locally):
handler> Re-evaluation result (locally):
[1] 1.609438
handler> IMPORTANT:
handler> Note how the expression evaluated is still
handler> myLog(x=5): 1.6094379124341when re-evaluated 'locally'
handler> inside the handler function.
handler> But also note that the **actual** return value
handler> of the handler or 'withCallingHandlers()'
handler> will be myLog(x=10): 2.302585 as the handler
handler> changed the value of 'x'!
The value of res
now is:
res
[1] 2.302585