Default arguments and lazy evaluation in R

In C++, default function arguments must be constants, but in R they can be much more general. For example, consider this R function definition.

    f <- function(a, b=log(a)) { a*b }

If f is called with two arguments, it returns their product. If f is called with one argument, the second argument defaults to the logarithm of the first. That’s convenient, but it gets more surprising. Look at this variation.

    f <- function(a, b=c) {c = log(a); a*b}

Now the default argument is a variable that doesn’t exist until the body of the function executes! If f is called with one argument, the R interpreter chugs along until it gets to the last line of the function and says “Hmm. What is b? Let me go back and see. Oh, the default value of b is c, and now I know what c is.”

This behavior is called lazy evaluation. Expressions are not evaluated unless and until they are needed. It’s a common feature of functional programming languages.

There’s a little bit of lazy evaluation in many languages: the second argument of a Boolean expression is often not evaluated if the expression value is determined by the first argument. For example, the function bar() in the expression

    while (foo() && bar())

will not be called if the function foo() returns false. But functional languages like R take lazy evaluation much further.

R was influenced by Scheme, though you could use R for a long time without noticing its functional roots. That speaks well of the language. Many people find functional programming too hard and will not use languages that beat you over the head with their functional flavor. But it’s intriguing to know that the functional capabilities are there in case they’re needed.

Related resources

4 thoughts on “Default arguments and lazy evaluation in R

  1. While it is true that R was influenced by the Lisp family of languages, Scheme (and most other Lisps, notably Common Lisp) use strict evaluation. Laziness is possible via add-ons (Lazy Scheme or the Series package in Common Lisp).

  2. Interesting fact. In other words, it says:

    “I’m too lazy to evaluate this now, may I postpone until you really need it?”:-)

    Just started to learn R and enjoying so far.

    Cesar

  3. Can you explain why `f(2, exp(a))` doesn’t work with your first definition of `f`? How does lazy eval know the difference between a default and non-default argument?

  4. Vincent Feltkamp

    @Ian ‘f(2, exp(a))’ doesn’t work because a is evaluated in the calling environment so a should be defined at the moment of calling, not inside the function.

Comments are closed.