adrian_m
adrian_m

Reputation: 41

Vectorize an S4 class in R

I got some troubles defining array like classes in a way that they are fully typed (as far as that is possible in R). My example: I want to define a class Avector, which should contain an arbitrary number of elements of the class A.

# Define the base class
setClass("A", representation(x = "numeric"))

# Some magic needed ????
setClass("Avector", ???)

# In the end one should be able to use it as follows:
a1 <- new("A", x = 1)
a2 <- new("A", x = 2)
X <- new("Avector", c(a1, a2))

I am aware that having a vector of objects is not possible in R. So I guess it will be stored in a kind of "typed" list. I have found some solution, but I am not happy with it:

# Define the vectorized class
setClass(
  "Avector", 
  representation(X = "list"), 
  valididty = function(.Object)) {
    if (all(sapply(.Object@X, function(x) class(x) == "A")))
      TRUE
    else
      "'Avector' must be a list of elements in the class 'A'"
  }
)
# Define a method to subscript the elements inside of the vector
setMethod(
  "[", signature(x = "Avector", i = "ANY", j = "ANY"),
  function(x, i, j, ...) x@X[[i]]
)
# Test the class
a1 <- new("A", x = 1)
a2 <- new("A", x = 2)
avec <- new("Avector", X = list(a1, a2))
# Retrieve the element in index i
avec[i]

This method appears more like a hack to me. Is there a way to do this in a canonical way in R without doing this type checking and indexing method by hand?

Edit: This should also hold, if the class A is not consisting of atomic slots. For example in the case that:

setClass("A", representation(x = "data.frame"))

I would be glad for help :) Cheers, Adrian

Upvotes: 4

Views: 861

Answers (1)

JDL
JDL

Reputation: 1654

The answer depends somewhat on what you are trying to accomplish, and may or may not be possible in your use case. The way S4 is intended to work is that objects are supposed to be high-level to avoid excessive overheads.

Generally, it is necessary to have the slots be vectors. You can't define new atomic types from within R. So in your toy example instead of calling

avec <- new("Avector", X = list(a1, a2))

you call

avec <- new("A", x = c(1, 2))

This may necessitate other slots (which were previously vectors) becoming arrays, for example.

If you're desperate to have an atomic type, then you might be able to over-ride one of the existing types. I think the bit64 package does this, for example. Essentially what you do is make a new class that inherits from, say, numeric and then write lots of methods that supersede all the default ones for your new class.

Upvotes: 1

Related Questions