12.3 Base types
While only OO objects have a class attribute, every object has a base type:
typeof(1:10)
#> [1] "integer"
typeof(mtcars)
#> [1] "list"
Base types do not form an OOP system because functions that behave differently for different base types are primarily written in C code that uses switch statements. This means that only R-core can create new types, and creating a new type is a lot of work because every switch statement needs to be modified to handle a new case. As a consequence, new base types are rarely added. The most recent change, in 2011, added two exotic types that you never see in R itself, but are needed for diagnosing memory problems. Prior to that, the last type added was a special base type for S4 objects added in 2005.
In total, there are 25 different base types. They are listed below, loosely grouped according to where they’re discussed in this book. These types are most important in C code, so you’ll often see them called by their C type names. I’ve included those in parentheses.
Vectors, Chapter 3, include types
NULL
(NILSXP
),logical
(LGLSXP
),integer
(INTSXP
),double
(REALSXP
),complex
(CPLXSXP
),character
(STRSXP
),list
(VECSXP
), andraw
(RAWSXP
).typeof(NULL) #> [1] "NULL" typeof(1L) #> [1] "integer" typeof(1i) #> [1] "complex"
Functions, Chapter 6, include types
closure
(regular R functions,CLOSXP
),special
(internal functions,SPECIALSXP
), andbuiltin
(primitive functions,BUILTINSXP
).typeof(mean) #> [1] "closure" typeof(`[`) #> [1] "special" typeof(sum) #> [1] "builtin"
Internal and primitive functions are described in Section 6.2.2.
Environments, Chapter 7, have type
environment
(ENVSXP
).typeof(globalenv()) #> [1] "environment"
The
S4
type (S4SXP
), Chapter 15, is used for S4 classes that don’t inherit from an existing base type.stats4::mle(function(x = 1) (x - 2) ^ 2) mle_obj <-typeof(mle_obj) #> [1] "S4"
Language components, Chapter 18, include
symbol
(aka name,SYMSXP
),language
(usually called calls,LANGSXP
), andpairlist
(used for function arguments,LISTSXP
) types.typeof(quote(a)) #> [1] "symbol" typeof(quote(a + 1)) #> [1] "language" typeof(formals(mean)) #> [1] "pairlist"
expression
(EXPRSXP
) is a special purpose type that’s only returned byparse()
andexpression()
. Expressions are generally not needed in user code.The remaining types are esoteric and rarely seen in R. They are important primarily for C code:
externalptr
(EXTPTRSXP
),weakref
(WEAKREFSXP
),bytecode
(BCODESXP
),promise
(PROMSXP
),...
(DOTSXP
), andany
(ANYSXP
).
You may have heard of mode()
and storage.mode()
. Do not use these functions: they exist only to provide type names that are compatible with S.
12.3.1 Numeric type
Be careful when talking about the numeric type, because R uses “numeric” to mean three slightly different things:
In some places numeric is used as an alias for the double type. For example
as.numeric()
is identical toas.double()
, andnumeric()
is identical todouble()
.(R also occasionally uses real instead of double;
NA_real_
is the one place that you’re likely to encounter this in practice.)In the S3 and S4 systems, numeric is used as a shorthand for either integer or double type, and is used when picking methods:
::s3_class(1) sloop#> [1] "double" "numeric" ::s3_class(1L) sloop#> [1] "integer" "numeric"
is.numeric()
tests for objects that behave like numbers. For example, factors have type “integer” but don’t behave like numbers (i.e. it doesn’t make sense to take the mean of factor).typeof(factor("x")) #> [1] "integer" is.numeric(factor("x")) #> [1] FALSE
In this book, I consistently use numeric to mean an object of type integer or double.