Search code examples
rjuliar-s4abstract-data-type

Abstract types in R


I want to create class in R, let's say it is an S4 class for a person. E.g.

setClass("Person", slots = list(name = "character", mood = "myMoodType"))

Now I want to create myMoodType to be an abstract type that can only take the three values "Happy", "Sad" and "Unknown".

I know that I could do this using the validity for S4 classes and have the mood as a character type and check the validity by verifying that the character string provided is one of the three options I list. But I would like to know if I can define an abstract type, like in julia, e.g.

abstract myMoodType
type Happy   <: myMoodType             end
type Sad     <: myMoodType             end
type Unknown <: myMoodType             end

What would be the correct way to approach this in R?


Solution

  • This might not be one of R's strongest and most smooth feature, but you could solve it in the following way. For more information see the documentation or the Advanced R chapter on S4.

    First set up the Person class with the mood represented as a factor, and link it to a validation function that checks its levels.

    check_person <- function(object) {
        if(identical(levels(object@mood), c("Happy", "Sad", "Unknown"))){
            return(TRUE)
        } else {
            return("Invalid mood.")
        }
    }
    
    setClass("Person",
             representation(name = "character", mood = "factor"),
             prototype = list(name = NA_character_,
                              mood = factor(NA, c("Happy", "Sad", "Unknown"))),
             validity = check_person)
    

    Creating new instances with new is however a bit messy since we have to write out all the levels each time:

    john <- new("Person", name="John", mood=factor("Happy", levels=c("Happy", "Sad", "Unknown")))
    lucy <- new("Person", name="Lucy", mood=factor("Sad", levels=c("Happy", "Sad", "Unknown")))
    

    Otherwise we'll get an error:

    new("Person", name="Eve", mood="Unknown")
    
    Error in validObject(.Object) : 
      invalid class “Person” object: invalid object for slot "mood" in class "Person":
      got class "character", should be or extend class "factor"
    

    To get around that you could make your own constructor:

    new_person <- function(name, mood){
        new("Person", name = name, mood = factor(mood, levels = c("Happy", "Sad", "Unknown")))
    }
    new_person("Eve", "Unknown")
    
    An object of class "Person"
    Slot "name":
    [1] "Eve"
    
    Slot "mood":
    [1] Unknown
    Levels: Happy Sad Unknown