Friday, November 20, 2015

R Basics 11 - OOP and R5

What is object oriented programming?
While definitions for OOP abound without clear agreement, OOP languages typically focus programers on the actors/objects(none) of a problem 
rather than the actions/procedures(verbs), by using a common set of language features, including:
1) Encapsulation data and code - the data and the code that manages that data are kept together by the language( in classes, models or clusters, etc.) Implicitly, this includes the notion of class definitions and class instances.
2) Information hiding - an exposed API with a hidden implementation of code and data; encourages programming by contract
3) Abstraction and Inheritance - so that similarities and difference in the underlying model/data/code/logic for related objects can be grouped  & reused
4) Dynamic dispatch - more than one method with the same name - where the method used is selected at compile or run-time by the class of the object and also the class of the method parameter types and their arity (argument number).

Four R Mechanisms with some OOP features
1) Lexical scoping 
- simple 
- encapsulation 
- mutability 
- information hiding, BUT not real classes 
- no inheritance
2) S3 classes 
- multiple dispatch on class only 
- inheritance, BUT just a naming convention
- no encapsulation 
- no information hiding 
- no control over use 
- no consistency checks 
- easy to abuse
3) S4 formal classes 
- multiple inheritance 
- multiple dispatch 
- inheritance 
- type checking, BUT no information hiding 
- verbose and complex to code 
- lots of new terms 
- immutable classes only
4) R5 reference classes 
- built on S4 
- mutable ( more like Java, C++) 
- type checking 
- multiple inheritance, BUT no information hiding 
- inconsistent with R's functional programming heritage
Note: None of R's OOP systems are as full featured or as robust as Java or C++.

What are S3 classes
# An S3 class is any R object to which a class attribute has been attached.

S3 classes - key functions
class(x)
class(x) <-'name'
methods('method')
UseMethod('method', x)
NextMethod()

Class code example
c.list <- list(hrs=12, mins=0, diem='am')
clas(c.list)
class(c.list)
clock <- structure(list(hrs=12, mins=0, diem="am"), .Names= c("hrs", "mins", "diem"), class = "clock")
c.list <- unclass(c.list)
attr(c.list, 'class')

Dynamic dispatch - UseMethod()
# the UseMehod for print already exists:
# print <- function(x) UseMehod('print', x)
print.clock <- function(x) {
cat(x$hrs);
cat(':')
cat(sprintf('%02d', x$mins));
cat(' ');
cat(x$diem);
cat('\n');
}
print(c.list)
methods('print')

Inheritance dispatch - NExtMethod()
# S3 classes allow for a limited form of class inheirtance fo the purposes of method dispatch. Try the following code:
sound <- function(x) UseMehod('sound', x)
sound.animal <- function(x) NextMethod()
sound.human <- function(x) 'conversation'
sound.cat <- function(x) 'meow'
sound.default <- function(x) 'grunt'
Cathy <- list(legs=4)
class(Cathy) <- c('animal', 'cat')
Harry <- list(legs=4)
class(Leroy) <- c('animal', 'llama')
sound(Cathy)
sound(Harry)
sound(Leroy)

Should I use S3 or S4 or R5?
S3: for amall/medium projects;
S4 for larger;
R5 if mutability is necessary

R5 Reference Classes

Summary of some key class mechanisms
1) create/get object-generator:
gen <- setRefClass('name', fields=, contains=, methods=, where,...)
enf <- getRefClass('name') - generator
gen$lock('fieldName') - lock a field
gen$help(topic) - get help on the class
gen$methods(...) - add methods to class
gen$methods() - get a list of methods
gen$fileds() - get a list of fields
gen$accessors(...) - create get/set fns

2) generator object used to get instance:
inst <- gen$new(...) - instantiation parameters passed to initialize(...)
inst$copy(shallow=F) - copy instance
inst$show() - called by print
inst$field(name, value) - set
inst$field(name) - get
is(inst 'envReflass') - is R5 test
[envRefClass is the super class for R5]

3) code from within your methods
initialize(...) instance initializer
finalize() - called by garbage collector
.self -reference to the self instance
.refClassDef -the class definition
methods::show() - call the show function
callSuper(...) - call the same method in the super class
.self$classVariable <- localVariable
classVariable <<- local Variable
globalVariable <<- localVariable
.self$classVariable <- localVariable
.self$field(clasVar, localVar) # set
.self$field(classVar, localVar) # get
Trap: very easy to confuse <- and <<-
Trap: if x is not a class field; x<<- var assigns to x in global environment

Field list - code sample
A <- setRefClas('A', 
fields = list(
# 1. typed, instance filed:
exampleVar1 = 'character',
# 2. instance file with accessor:
ev2.private='character',
exampleVar2 = function(x) {
if(!missing(x)) ev2.private <<- x
ev2.private
}
),methods = list(
initialize=function(c='default') {
exampleVar1 <<- c
exampleVar2 <<- c
}
)
)
instA <_ A$new('instnce of A');
str(instA)

Inheritance code sample
Animal <- setRefClass('Animal', 
# virtual super class
contains = list('VIRTUAL'),
fields = list(i.am = 'character', noiseMakes = 'character'),
methods = list(initialize=function(i.am='unknown', noiseMakes = 'unknown') {
.self$i.am <-i.am
.self$noiseMakes <- noiseMakes
},
show=function(){
cat('I am a');
cat(i.am)
cat('. I make this noise:')
cat(noiseMakes);
cat('\n')
}
)
)

Cat <- setRefCalss('Cat', 
contains = list('Aninmal'), 
methods = list(
initialize=function() callSuper('cat', 'meow'),
finalize=function() cat('Another cat passes.\n')
)
)

Dog <- setRefClass('Dog',
contains = list('Animal'),
methods = list(
initialize=function() callSuper('dog', 'woof'),
show = function() {
callSuper()
cat('I like to chew shoes. \n')
}
)
)

mongrel <- Animal$new()
fido = Dog$new();
felix =Cat$new()
print(fido);
print(felix)
felix <- NULL
gc()

What's neither C++ nor Java
1) No information hideing. Everthing is public and modifiable. (But the R package mechanism helps here).
2) No static class fields.
3) Not as developed or robust OOP space.

Tips(safer coding practices) and Traps
1) use named filed list to type variables
2) use accessor methods in the filed list to maintain class type & state validity
3) Trap:  methodName <- function() in methods list. Use = ( it's a named list!)
4) Trap: cant use enclosing environments within R5 classes as they are in one).

No comments:

Post a Comment

Blog Archive