retList classes

retList can be used as a shortcut to:

constructor <- function(...) {
  ...
  out <- list(...)
  class(out) <- "constructor"
  out
}

vs.

constructor <- function(...) {
  ...
  retList("constructor")
}

It will simply find all objects in the environment in which it is called and returns those in a list. This is one way to think about and implement features associated with object orientation in R.

Basics

Every value bound to a symbol will be public, objects with a leading dot in the name will be private. This can be changed by stating which names to make public explicitely.

Here is a simple class definition:

library("aoos")

Person <- function(name) {
  
  print <- function(x, ...) {
    cat(paste0("Hello, my name is ", .self$name, ".\n"))
  }
  
  retList(c("Person", "Print"))
}

retList is used as a generic constructor function. It allways returns a list containing all visible values from the environment where it is called. There is a print method print.Print in the package which will look for a member function called print for printing. Thus the print method in the class Person will be used as S3 printing method.

ann <- Person("Ann")
ann
## Hello, my name is Ann.

Inside the class definition - i.e. the body of the constructor - you can use .self but you do not have to; It just might be more explicit. You can also use the super-assignment operator <<-. Note that .self is not a reference to the instance itself, it is only referencing to the environment in which the methods will live in.

You should keep in mind that a public field is a value in a list and not a reference to the value in .self. Thus, if your object should have public fields you have to define get/set methods. In this example you can think of name as an attribute since changing it in an instance won’t change the bahavior of greet. Here is something you can do instead:

Person <- function(.name) {
  
  print <- function(x, ...) {
    cat(paste0("Hello, my name is ", .self$.name, ".\n"))
  }
  
  name <- function(x) {
    if (!missing(x)) .name <<- x
    .name
  }
  
  retList(c("Person", "Print"))
}

p <- Person("Ann")
p
## Hello, my name is Ann.
p$name()
## [1] "Ann"
p$name("Paul")
## [1] "Paul"
p
## Hello, my name is Paul.

Although I don’t understand why ‘Ann’ can change to be ‘Paul’, this is one way of adding something like reference semantics.

Inheritance

An object can inherit from another object. The constructer then extends the instance of the super class/object. For that you can supply a call to the super classes constructor function, where you also have to supply arguments for initialization.

Person <- function(name) {
  
  print <- function(x, ...) {
    cat(paste0("Hello, my name is ", .self$name, ".\n"))
  }
  
  retList(c("Person", "Print"))

}

Employee <- function(id, ...) {
  
  print <- function(x, ...) {
    cat("Name: ", name, "\nID:   ", id)
  }
  
  retList("Employee", super = Person(...))
  
}

kalle <- Employee("1", "Kalle")
str(kalle)
## List of 3
##  $ id   : chr "1"
##  $ print:function (x, ...)  
##   ..- attr(*, "srcref")=Class 'srcref'  atomic [1:8] 13 12 15 3 12 3 13 15
##   .. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x1f5424b0> 
##  $ name : chr "Kalle"
##  - attr(*, ".self")=<environment: 0x1e6bb6e0> 
##  - attr(*, "class")= chr [1:4] "Employee" "Person" "Print" "list"
kalle
## Name:  Kalle 
## ID:    1