Type speciï¬cation for your functions
cellpadding=0 cellspacing=0 width=100%>
Yahoo! is not affiliated with the authors of this page or responsible for its content.
Type specication for your functions
TYPE SPECIFICATION FOR YOUR FUNCTIONS
Type specication for your functions
by MT Morgan, Seth Falcon, Robert Gentleman, and
Duncan Temple Lang
Youve written some amazing R functions. How can
others, even non-R users, benet from your hard
work? Maybe you can make it easy for other pro-
grammers to learn about the arguments and return
values of your function? Perhaps a web-based form
or dialog box, like those provided by widgetInvoke,
would help users choose appropriate arguments?
These objectives are easier to obtain when R func-
tions provide information about themselves.
The TypeInfo package annotates functions with
information about argument and return types. Type-
Info
automatically checks that your function is called
with appropriate arguments. You can then focus on
writing the code in the body of your function, rather
than checking values supplied by users. Other R pro-
grammers can ask functions about their argument
and return types. This reection opens the door to
creative possibilities, for instance automatically cre-
ating a work ow (perhaps a graphical wizard?)
chaining function calls together into a complicated
overall analysis.
This article illustrate how to use TypeInfo to
specify argument and return types. We start with
straight-forward ways of applying type information.
Then we illustrate the exibility of TypeInfo for ap-
ply complicated type checks, including types satis-
fying arbitrary R expressions. The article concludes
with a brief look behind the scenes to expose limita-
tions of TypeInfo, and to highlight opportunities for
using typed functions in advanced aspects of your
own work.
The basics: applying typeInfo
Suppose your colleagues clamor for a function to
perform one-way analysis of variance on data where
the predictor is a factor. Easily done in R with
lm
, but the R formula notation might be more
than needed for our simple function. To help our
colleagues, we simplify the interface to refer to
response
and predictor variables. Many users ex-
pect an ANOVA table as output, so we return the re-
sult of anova rather than lm. Here is our function:
> oneWayAnova <- function(response,
+
predictor) {
+
expr <- substitute(response ~
+
predictor)
+
result <- lm(as.formula(expr))
+
anova(result)
+ }
> copyOfOneWayAnova <- oneWayAnova
We make a copy of the function denition to conve-
niently re-apply different type information, as will
become apparent below. To test our function, we use
data from the help page for lm:
> ctl <- c(4.17, 5.58, 5.18, 6.11, 4.5,
+
4.61, 5.17, 4.53, 5.33, 5.14)
> trt <- c(4.81, 4.17, 4.41, 3.59, 5.87,
+
3.83, 6.03, 4.89, 4.32, 4.69)
> group <- gl(2, 10, 20, labels = c("Ctl",
+
"Trt"))
> weight <- c(ctl, trt)
> oneWayAnova(weight, group)
Analysis of Variance Table
Response: weight
Df Sum Sq Mean Sq F value Pr(>F)
group
1 0.6882
0.6882
1.4191
0.249
Residuals 18 8.7292
0.4850
Applying type checks
We want to make sure our users enter the right kinds
of arguments. To do this, we add type information to
make sure that response is numeric, and predictor
a factor. We start by loading the TypeInfo library. . .
> library(TypeInfo)
and, after dening oneWayAnova, add type informa-
tion:
> typeInfo(oneWayAnova) <-
+
SimultaneousTypeSpecification(
+
TypedSignature(
+
response = "numeric",
+
predictor = "factor"),
+
returnType = "anova")
The command SimultaneousTypeSpecification
means that we want to impose a set of conditions
that apply simultaneously to all arguments (and,
optionally, the return type). TypedSignature cor-
responds to one set of conditions. The structure of
TypedSignature
is a list of argument names and
their types. Specifying returnType advertises that
our function returns an object of type anova, and
checks that it really does return this type. The rea-
son for placing returnType after TypedSignature is
claried below.
Using a typed function is exactly the same as us-
ing an untyped function:
> oneWayAnova(weight, group)
Analysis of Variance Table
Response: weight
1
FLEXIBLE TYPEINFO
TYPE SPECIFICATION FOR YOUR FUNCTIONS
Df Sum Sq Mean Sq F value Pr(>F)
group
1 0.6882
0.6882
1.4191
0.249
Residuals 18 8.7292
0.4850
Finding out about type information
Once applied, functions can be queried with
typeInfo
.
> typeInfo(oneWayAnova)
The output, in Figure 1, illustrates how type in-
formation is stored. typeInfo returns an object of
class SimultaneousTypeSpecification. The object
contains a list of objects of class TypedSignature,
and a returnType slot. Each TypedSignature is a list
with entries for each element with type specication.
The information returned from typeInfo pro-
vides useful information as-is. It can also be parsed
by computer code to provide information useful in
creation of graphical widgets or other interfaces.
Incorrect arguments
When the user supplies incorrect data, e.g., repre-
senting the predictor as numeric rather than factor
> ngroup <- as.numeric(group)
> res <- try(oneWayAnova(weight, ngroup))
TypeInfo
intervenes with the error show in Figure 1.
Providing TypeInfo is helpful, as our user might
otherwise have performed a linear regression rather
than xed-effects ANOVA.
Elaborating on type signatures
We might decide that, for our purposes, the
predictor
can be either factor or numeric.
We change the SimultaneousTypeSpecification
to include another TypedSignature (re-applying
typeInfo
does not automatically overwrite previous
type specications, so we must use typeInfo on a
fresh version of oneWayAnova):
> oneWayAnova <- copyOfOneWayAnova
> typeInfo(oneWayAnova) <-
+
SimultaneousTypeSpecification(
+
TypedSignature(
+
response = "numeric",
+
predictor = "factor"),
+
TypedSignature(
+
response = "numeric",
+
predictor = "numeric"),
+
returnType = "anova")
This starts to show the exibility of Type-
Info
.
SimultaneousTypeSpecification
allows
for more than one TypedSignature.
At least
one of the TypedSignatures must be correct for
the function to be evaluated.
Conceptually,
SimultaneousTypeSpecification
performs a logi-
cal OR operation across the TypedSignatures. On
the other hand, each TypedSignature species con-
ditions that must all apply. TypedSignature per-
forms a logical AND on its elements. In the exam-
ple here, regardless of argument type, the function
returns an object of class anova.
Flexible TypeInfo
The presentation so far emphasizes the sort of ba-
sic type specication that is likely to be most use-
ful when making R functions available to other pro-
gramming languages. TypeInfo offers a range of
methods for validating type that can be very use-
ful for R programmers, but that employ concepts not
readily translated into other languages. A sampling
of these are presented here, along with additional de-
tail about the application of type specication .
InheritsTypeTest
Notice in the example above that arguments are la-
beled with character strings of type names. A type
specication of class character corresponds to an
InheritsTypeTest
, as indicated explicitly for the
returnType
specication. An InheritsTypeTest re-
quires that the object belong to, or extends, the speci-
ed class. For instance, the values passed to the func-
tion in the response variable must return TRUE from
the test is(response, "numeric"). Because of this,
oneWayAnova
works with response as either numeric
or integer.
> iweight <- as.integer(weight)
> oneWayAnova(iweight, group)
Analysis of Variance Table
Response: iweight
Df
Sum Sq Mean Sq F value Pr(>F)
group
1
1.8000
1.8000
2.9455 0.1033
Residuals 18 11.0000
0.6111
StrictIsTypeTest
and DynamicTypeTest
What other ways does TypeInfo offer to spec-
ify type?
StrictIsTypeTest
requires an exact
match between the class of an object and the spec-
ied class(es).
To specifying a strict match for
response
and returnValue, but an inherited match
for predictor, write
> oneWayAnova <- copyOfOneWayAnova
> typeInfo(oneWayAnova) <-
+
SimultaneousTypeSpecification(
+
TypedSignature(
+
response = StrictIsTypeTest("numeric"),
+
predictor = InheritsTypeTest("factor")),
2
BEHIND THE SCENES
TYPE SPECIFICATION FOR YOUR FUNCTIONS
> typeInfo(oneWayAnova)
[SimultaneousTypeSpecification]
[TypedSignature]
response: is(response, c(
'numeric')) [InheritsTypeTest]
predictor: is(predictor, c(
'factor')) [InheritsTypeTest]
returnType: is(returnType, c(
'anova')) [InheritsTypeTest]
> ngroup <- as.numeric(group)
> res <- tryCatch(oneWayAnova(weight,
+
ngroup), error = function(err) {
+
cat("Error:", conditionMessage(err),
+
"\n")
+ })
Error: TypeInfo could not match signature.
Supplied arguments and their types:
response: numeric
predictor: numeric
Available signature(s):
[SimultaneousTypeSpecification]
[TypedSignature]
response: is(response, c(
'numeric')) [InheritsTypeTest]
predictor: is(predictor, c(
'factor')) [InheritsTypeTest]
returnType: is(returnType, c(
'anova')) [InheritsTypeTest]
Figure 1: Finding out about type information, and the informative consequences of supplying incorrect argu-
ments.
+
returnType = StrictIsTypeTest("anova"))
> oneWayAnova(iweight, grou