lapply() is a really useful function but it has some limitations. For example, with lapply() there is no way to:
- add new elements
- remove existing elements
- change the names of list elements
To overcome these problems we could create a variant of lapply() than I'm going to call fapply() (where f is for flexible). Rather than calling the supplied function with with an element of the list extract with [[, it will call it with a sub-list extracted with [.
fapply <- function(x, f, ...) {
out <- vector("list", length(x))
for (i in seq_along(x)) {
res <- f(x[i], ...)
stopifnot(is.list(x) && is.vector(x))
out[[i]] <- res
}
unlist(out, recursive = FALSE)
}
We can then use fapply() to:
-
Change names:
x <- list(a = 1, b = 2, c = 3) fapply(x, function(x) setNames(x, toupper(names(x)))) -
Add new elements to the list:
x <- list(a = 1, b = 2, c = 3) fapply(x, function(x) rep(x, x[[1]])) -
Remove elements from the list:
x <- list(a = 1, b = 2, c = 3) fapply(x, function(x) if (x[[1]] > 2) x)
The difference between lapply() and fapply() is a lot like the difference bewteen [[ and [. In lapply(), f takes an element of a list and returns a new element; in fapply(), f takes a sublist and returns a sublist. An important property of fapply() is the type of object that f takes as input and returns as output is the same as the original x, a list.
Monads are a generalisation of this idea. If you have an object, like a list, that provides a function that works like fapply(), then it's a monad. It turns out that you can implement fapply() in terms of some simpler functions, which are what monads are usually defined in terms of.