No, we are not talking about dinner, although this post is giving me ideas about what I should have for lunch.

To read about what function currying actually means, please see the Wikipedia page. (And while you are there, make a donation already!)

Basically, you take a function of multiple arguments and reduce it to a function with one argument. I probably use the term incorrectly, because I also consider currying to cover any reduction in the number of function arguments, not just down to one.

Here’s an example. I’m using the curry function from my personal R package.

library(alrtools)
my_rnorm <- curry(rnorm, mean = 100, sd = 200)
print(my_rnorm)
## function (n, mean = 100, sd = 200) 
## .Call(C_rnorm, n, mean, sd)
## <environment: namespace:stats>

Now, every time I use my_rnorm the parameters mean and sd are pre-set.

set.seed(95)
x <- my_rnorm(100e3)
mean(x)
## [1] 99.52828
sd(x)
## [1] 199.7653

NOTE: my curry function still get a weird error with some functions that are very general in the arguments they accept, like max and sum. I’m still figuring that out.

There are other packages in R that support currying. Namely functional.

library(functional)
f_rnorm <- Curry(rnorm, mu = -20, sd = 35)
print(f_rnorm)
## function (...) 
## do.call(FUN, c(.orig, list(...)))
## <environment: 0x0000000018982178>

Peter Danenberg, the author of the functional package, credits Byron Ellis for the functional::Curry function. I have an older version of alrtools:::curry_v1 that I copied from the same source. Fun times.

So, if this has existed since 2007, why am I blogging about it? Because I finally solved something annoying about Curry and I just can’t wait to share (with the two people who will read this).

Check out the function signatures for the two curried functions and the original:

args(f_rnorm)
## function (...) 
args(my_rnorm)
## function (n, mean = 100, sd = 200) 
args(rnorm)
## function (n, mean = 0, sd = 1) 

f_rnorm doesn’t tell you anything about the arguments that are left, or the defaults that were set in the currying process. Most of the time, no one cares because you just created the curried function so why should you have forgotten the details about it? Yes, true, but I want to use alrtools::curry to provide code for other people to use, and they might like the visual cues.

Here are some cool things about my new version of curry

  1. The function signature shows all arguments
  2. Currying changes the function signature and simply includes defaults for the curried arguments
  3. You can add new arguments that before were not in the signature!
  4. You can override the curried arguments if you want (because the currying is done by specifying default values instead of storing the default values in the closure)
  5. The new function stays in the namespace of the original function, but is still listed in the global environment
  6. But, remember the caveat I gave above – I am still working through all the issues with dots
  7. I haven’t tested this yet, but I am pretty sure that curried arguments will be able to reference other arguments, like in the function signature of dgamma

Just a note for those of you who like this topic but would rather use a more reputable package, like functional. You can still get at all the information you want within the function closure. R let’s you examine those, and I will show you how. (Also, my package is licensed as CC0, so you can steal the code and use it wherever you want, too. I don’t care. Attribution is nice.)

max100 <- functional::Curry(max, 100)
max100(c(40, 20, -21))
## [1] 100
max100(150, 34, -132)
## [1] 150
ls(environment(max100), all.names = TRUE)
## [1] "..."   ".orig" "FUN"
environment(max100)$.orig
## [[1]]
## [1] 100
environment(max100)$FUN
## function (..., na.rm = FALSE)  .Primitive("max")

Thanks for reading!