Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

S4 constructors and prototypes

Tags:

constructor

r

s4

Looking through Hadley Wickham's S4 wiki: https://github.com/hadley/devtools/wiki/S4

setClass("Person", representation(name = "character", age = "numeric"), 
  prototype(name = NA_character_, age = NA_real_))
hadley <- new("Person", name = "Hadley")

How can we design a constructor for Person (like this)

Person<-function(name=NA,age=NA){
 new("Person",name=name,age=age)
}

that doesn't do this:

> Person()
Error in validObject(.Object) : 
  invalid class "Person" object: 1: invalid object for slot "name" in class "Person": got class "logical", should be or extend class "character"
invalid class "Person" object: 2: invalid object for slot "age" in class "Person": got class "logical", should be or extend class "numeric"
like image 637
Jeremy Leipzig Avatar asked Oct 21 '11 17:10

Jeremy Leipzig


People also ask

What is S4 method?

S4 provides a formal approach to functional OOP. The underlying ideas are similar to S3 (the topic of Chapter 13), but implementation is much stricter and makes use of specialised functions for creating classes ( setClass() ), generics ( setGeneric() ), and methods ( setMethod() ).

How do you create S4 objects How can you check an object is S4 object?

We can check if an object is an S4 object through the function isS4() . The function setClass() returns a generator function. This generator function (usually having same name as the class) can be used to create new objects. It acts as a constructor.

What is a constructor function in R?

One of the most used methods for object-oriented programming in R is the S3 system. In R, you can convert a list to a class definition. R also lets you create a constructor, which is a function that runs each time you create a new object (instance of a class). You're also able to create methods and functions in R.


2 Answers

It looks like the answer is right there in your example:

Person<-function(name=NA_character_,age=NA_real_){
 new("Person",name=name,age=age)
}

yields

> Person()
An object of class "Person"
Slot "name":
[1] NA

Slot "age":
[1] NA

> Person("Moi")
An object of class "Person"
Slot "name":
[1] "Moi"

Slot "age":
[1] NA

> Person("Moi", 42)
An object of class "Person"
Slot "name":
[1] "Moi"

Slot "age":
[1] 42

However, that is fairly un-S4 and duplicates the default values already assigned in the class definition. Maybe you'd prefer to do

Person <- function(...) new("Person",...)

and sacrifice the ability to call without named arguments?

like image 174
themel Avatar answered Nov 05 '22 05:11

themel


I'd prefer giving the end-user some hints about argument types than the suggestion to use ... by @themel. Also forgoing the prototype and using length(x@name) == 0 as an indication that the field is un-initialized, using a People class rather than Person, reflecting the vectorized structure of R, and using ... in the constructor so derived classes can also use the constructor.

setClass("People",
    representation=representation(
        firstNames="character",
        ages="numeric"),
    validity=function(object) {
        if (length(object@firstNames) != length(object@ages))
            "'firstNames' and 'ages' must have same length"
        else TRUE
    })

People = function(firstNames=character(), ages=numeric(), ...)
    new("People", firstNames=firstNames, ages=ages, ...)

And

People(c("Me", "Myself", "I"), ages=c(NA_real_, 42, 12))
like image 29
Martin Morgan Avatar answered Nov 05 '22 05:11

Martin Morgan