Konrad
Konrad

Reputation: 18595

Understanding difference between attr(x, "class") and class(x)

Background

I'm looking at the jazz package proposed by Romain François. Romain defines a function is_bare_vector with the following syntax:

is_bare_vector <- function(x) {
  is_vector(x) && !is.object(x) && is.null(attr(x, "class"))
}

Question

For: x <- 1:

attr(x, "class")
# NULL

Whereas:

class(x)
# [1] "numeric"

I want to understand why those two functions provide different answers? The help on ?attr references ?class

?attr

Note that some attributes (namely class, comment, dim, dimnames, names, row.names and tsp) are treated specially and have restrictions on the values which can be set. (Note that this is not true of levels which should be set for factors via the levels replacement function.)

?class

Many R objects have a class attribute, a character vector giving the names of the classes from which the object inherits. (Functions oldClass and oldClass<- get and set the attribute, which can also be done directly.)

Upvotes: 10

Views: 382

Answers (1)

duckmayr
duckmayr

Reputation: 16940

You just need to read on a little further in help("class"):

If the object does not have a class attribute, it has an implicit class, notably "matrix", "array", "function" or "numeric" or the result of typeof(x) (which is similar to mode(x))

Apparently class() will also return the implicit class if the class attribute is NULL; let's examine the C source code -- we see that if the class attribute is of length 0, it gets the implicit class.:

SEXP R_data_class(SEXP obj, Rboolean singleString)
{
    SEXP value, klass = getAttrib(obj, R_ClassSymbol);
    int n = length(klass);
    if(n == 1 || (n > 0 && !singleString))
    return(klass);
    if(n == 0) {
    SEXP dim = getAttrib(obj, R_DimSymbol);
    int nd = length(dim);
    if(nd > 0) {
        if(nd == 2)
        klass = mkChar("matrix");
        else
        klass = mkChar("array");
    }
    else {
      SEXPTYPE t = TYPEOF(obj);
      switch(t) {
      case CLOSXP: case SPECIALSXP: case BUILTINSXP:
        klass = mkChar("function");
        break;
      case REALSXP:
        klass = mkChar("numeric");
        break;
      case SYMSXP:
        klass = mkChar("name");
        break;
      case LANGSXP:
        klass = lang2str(obj, t);
        break;
      default:
        klass = type2str(t);
      }
    }
    }
    else
    klass = asChar(klass);
    PROTECT(klass);
    value = ScalarString(klass);
    UNPROTECT(1);
    return value;
}

Upvotes: 8

Related Questions