R-beta: Re: S Compatibility

Ross Ihaka ihaka at stat.auckland.ac.nz
Wed Apr 30 03:24:52 CEST 1997


Bill Venables writes:

> Are the scoping differences between R and S set out precisely and
> definitively somewhere?  This would be useful.

In the source code perhaps? :-)

You can find a pretty precise description in the article Robert and I
did in JCGS.

Actually its pretty simple.  Functions have access to the variables
which were in effect when the function was defined.

	f <- function(x) {
		g <- function() {
			x * x
		}
		g()
	}

The body of g has access to "x" even though it is not global and not
passed as an argument.

Passing values in this way is actually much more efficient than passing
via a formal argument (in R and I presume S) because the matching of
formals and actuals is quite expensive.

Hmmm.  Actually my statement above is not quite precise.  Functions
have access to variables in the environment which was in effect when
the function was defined, so

	f <- function(x)
		g <- function() {
			y * y
		}
		y <- x/2
		g()
	}

will also work, even though y is defined after g, because g and y
are defined in the same environment (the body of f).

One really useful thing you can do with this is to create functions
with "own" or static variables.  Consider

	make.linear.fun <- function(a, b) {
		function(x) a + b * x
	}

	g <- make.linear.fun(2,3)

After this, g is a function of a single variable "x", but the
the body of g has access to the values of "a" and "b" which
were in effect when the function was created.

This remains true even after "make.linear.fun" has returned.  Essentially,
g has access to variables "a" and "b" which are not connected to the
rest of the universe.

"make.linear.fun" can be used to make up lots of linear functions and
they all have their own private copies of "a" and "b".

If you look at our function "splinefun", you'll see this in action.
Unlike "spline" which carries out the spline interpolation, "splinefun"
returns a function which will do the interpolation - i.e. the spline
itself.  You can also create likelihoods which encapsulate their
data etc.  It lets you do things much more abstractly.

We've also adapted the operator <<- so that it works back up through
the environment stack and carries out the assigment on the first
variable it finds with the right name.

	mk.counter <- function(count=0)
			function() {
				count <<- count + 1
				count
			}

	counter1 <- mk.counter()	# each has its own count
	counter2 <- mk.counter()

	counter1() # -> returns 1
	counter1() # -> returns 2
	counter2() # -> returns 1
	...

This gives functions what is called "mutable state" and could be used to
create a real object system (the instance variables would really be
private).

We haven't really made a big deal about this, but I think it is an
incredibly elegant idea.  I wish we'd thought of it, but we just borrowed
it from Scheme.

	(As a complete side-issue, Brian Ripley and I have a kind of
	convention: we refer to the language as "S" and the commercial
	product as "S-PLUS".  There is a useful distinction to be made.)

This is generally what I try to do too.

However, I suspect though that most people don't know that there is a
difference, and these days so few people can get access to the non-plussed
version, I wonder how important it is to make the distinction.  :-(

	Ross
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
r-help mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
Send "info", "help", or "[un]subscribe"
(in the "body", not the subject !)  To: r-help-request at stat.math.ethz.ch
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=



More information about the R-help mailing list