R Error messages: A visual overview
R Error message: object of type 'closure' is not subsettable
A common error in R is object of type 'closure' is not subsettable
.
This message means that you have a variable which represents a function,
and you’re mistakenly using square brackets to try and subset it, thinking that
it represents a data.frame or vector or something e.g.
# `mean` here refers the built-in base R function
mean[1:3]
## Error in mean[1:3]: object of type 'closure' is not subsettable
In general if you get this error it means you’re trying to subset a function (or
somehow access an item within a function using $
) which (most of the time) doesn’t make much sense in
normal R code.
What if objects of type ‘closure’ were subsettable?
While riffing on a joke tweet, Luke Smith wondered on twitter if there was any actual case for make closures subsettable:
Can anyone think of an actual reason to do something like this? Looking at you @coolbutuseless https://t.co/1R9rHkrosE
— Luke Smith (@lksmth) February 8, 2019
The rest of this post is my idea for something vaguely useful you could do with subsetting a function/closure in R.
Using subset syntax to manipulate formal arguments of functions
- Aim: manipulate formal arguments of a function using
[ ]
or$
syntax - E.g: Use
rnorm$mean <- 100
to change the default mean for thernorm()
function.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Tell R that we want to make `[` and `$` generic
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`[` <- function(...) {UseMethod("[" )}
`[<-` <- function(...) {UseMethod("[<-")}
`$` <- function(...) {UseMethod("$" )}
`$<-` <- function(...) {UseMethod("$<-")}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#' Enable subset with [] on a function so we can:
#' Determine the current default value for a given formal argument
#'
#' e.g. rnorm['mean']
#'
#' @param func function
#' @param arg_name name of formal argument (character)
#'
#' @return current value of formal argument (or warning if not exists)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`[.function` <- `[.closure` <- function(func, arg_name) {
if (arg_name %in% names(formals(func))) {
formals(func)[[arg_name]]
} else {
warning("No such formal argument: ", arg_name)
}
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Same as above except allow: rnorm$mean
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`$.function` <- `$.closure` <- function(func, arg_name) {
func[as.character(substitute(arg_name))]
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Enable subset assignment on a function so we can:
# Set the given formal argument of the function to the new default value
#
#' e.g. rnorm['mean'] <- 100
#'
#' @param func function
#' @param arg_name name of formal argument (character)
#' @param value new default value for formal argument
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`[<-.function` <- `[<-.closure` <- function(func, arg_name, value) {
fargs <- formals(func)
fargs[[arg_name]] <- value
formals(func) <- fargs
func
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Same as above except allow: rnorm$mean <- 100
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`$<-.function` <- `$<-.closure` <- function(func, arg_name, value) {
func[as.character(substitute(arg_name))] <- value
func
}
Example: changing the default values of rnorm()
using function subsetting
# Find the default 'mean' for this function using '$' syntax
rnorm$mean
## [1] 0
# Set new values for the default `n` and `mean`
rnorm$n <- 5
rnorm$mean <- 100
# Looking at the function, we can see that the default arguments have been changed
rnorm
## function (n = 5, mean = 100, sd = 1)
## .Call(C_rnorm, n, mean, sd)
## <environment: namespace:stats>
# And calling it, runs it with the new defaults
rnorm()
## [1] 99.37355 100.18364 99.16437 101.59528 100.32951
Notes
- You can make objects of type ‘closure’ subsettable.
- If ‘closure’ objects are made subsettable, then you can reason about them and manipulate their arguments and code in a (possibly) intuitive manner.
- You’d probably never want to actually do this because it’s going to be super unsafe behaviour most of the time i.e. 99.9999999% of the cases, the user expects an error if they try and subset a function, not some weird thing to do with formal arguments as I’ve defined here.
- TL;DR - Fun, but don’t do it (unless you really want to)