Designing rsql: S3, S4 and expression evaluation in R

S4 for rsql

For a variety of reasons, some good and some bad, I wanted to use S4 classes for the table objects in rsql. Among other things, S4 gives you inheritance, prototypes, and some other neat stuff. Just as important to me, but totally superficial is the fact that the contents (ie slots) of an S4 object are retrieved using the ‘@’ operator, freeing the ‘$’ operator for fun.

Specifically, I decided to use the ‘$’ operator to access references to a table’s columns:

> tab$x
tab.x

This makes a variety of things much cleaner and more pleasant to use.

Problem: S4 evaluates arguments

I hadn’t had a lot of experience in writing S4 classes, so I was surprised to learn that it is not possible for S4 methods to have unevaluated arguments.

This is problematic since a wide variety of frequently used (S3) functions delay evaluation of their arguments for a specific context (usually a data frame). The most notable example of this is subset but there are plenty of others.

> dat = data.frame(x=rnorm(10),y=1:10)
> dat.sub = subset(dat,subset=(x>0))

Since arguments to S4 are evaluated while selecting an appropriate method, this is not possible. Instead, it becomes necessary to immediately capture the unevaluated expression using something like plyr::. in this example in the documentation for plyr::dcast:

#Air quality example
names(airquality) <- tolower(names(airquality))
aqm <- melt(airquality, id=c("month", "day"), na.rm=TRUE)

acast(aqm, variable ~ month, mean, subset = .(variable == "ozone"))

The end

So that’s what I did, too.

Addendum

This story is really uninteresting if you don’t realize how much work I put in to capture and pass those unevaluated expressions around before I needed to transition to S4 and it stopped working.

Comments