vignettes/clgen.Rmd
clgen.Rmd
Given a prototypical list of elementary R data it is trivial but tedious to define a valid S4 class with appropriate getter and setter methods. DrS4’s ClassSupport
class helps with this.
Stuart Lee’s blog post on S4 inspires the default parameter settings in the clgen
function.
suppressPackageStartupMessages(library(DrS4))
args(clgen)
## function (clname = "Turtle", protolist = list(location = c(0,
## 0), orientation = 0, path = matrix(c(0, 0), ncol = 2)), check_init = FALSE)
## NULL
Try it:
tt = clgen()
tt
## ClassSupport instance for Turtle
## slots: location orientation path
Learn a little more about this infrastructure:
getClass(class(tt))
## Class "ClassSupport" [package "DrS4"]
##
## Slots:
##
## Name: clname defcode getters setters protolist
## Class: character character list list list
We dump the code defining the class and its get/set methods.
dumpcs(tt)
## setClass('Turtle', slots= c(location='numeric', orientation='numeric', path='matrix'), prototype=list(location = c(0, 0), orientation = 0, path = structure(c(0, 0), .Dim = 1:2)))
## setGeneric(name='location', def=function(x) standardGeneric('location'))
## setMethod(f='location', signature='Turtle', function(x) slot(x, 'location'))
## setGeneric(name='orientation', def=function(x) standardGeneric('orientation'))
## setMethod(f='orientation', signature='Turtle', function(x) slot(x, 'orientation'))
## setGeneric(name='path', def=function(x) standardGeneric('path'))
## setMethod(f='path', signature='Turtle', function(x) slot(x, 'path'))
## setGeneric(name='location<-', def=function(x, value) standardGeneric('location<-'))
## setMethod(f='location<-', signature='Turtle', function(x, value) {slot(x, 'location') = value; x})
## setGeneric(name='orientation<-', def=function(x, value) standardGeneric('orientation<-'))
## setMethod(f='orientation<-', signature='Turtle', function(x, value) {slot(x, 'orientation') = value; x})
## setGeneric(name='path<-', def=function(x, value) standardGeneric('path<-'))
## setMethod(f='path<-', signature='Turtle', function(x, value) {slot(x, 'path') = value; x})
To use the code, we can source it, or place in a package.
## Creating a new generic function for 'path' in the global environment
## Creating a new generic function for 'path<-' in the global environment
tur = new("Turtle")
tur
## An object of class "Turtle"
## Slot "location":
## [1] 0 0
##
## Slot "orientation":
## [1] 0
##
## Slot "path":
## [,1] [,2]
## [1,] 0 0
path(tur)
## [,1] [,2]
## [1,] 0 0
## An object of class "Turtle"
## Slot "location":
## [1] 0 0
##
## Slot "orientation":
## [1] 0
##
## Slot "path":
## [,1] [,2]
## [1,] 1 1
Stuart showed how a more functional turtle can be defined – one that holds a pen. We’ll need a new definition and new getters and setters.
We defined a method on our ClassSupport
class to produce the new class.
newprops = list(colour = "pink", thickness = 1, on = FALSE)
nn = extendClassSupport(tt, newprops, "TurtleWithPen")
nn
## ClassSupport instance for TurtleWithPen
## slots: colour thickness on
dumpcs(nn)
## setClass('TurtleWithPen', contains='Turtle', slots=c(colour='character', thickness='numeric', on='logical'))
## setGeneric(name='colour', def=function(x) standardGeneric('colour'))
## setMethod(f='colour', signature='TurtleWithPen', function(x) slot(x, 'colour'))
## setGeneric(name='thickness', def=function(x) standardGeneric('thickness'))
## setMethod(f='thickness', signature='TurtleWithPen', function(x) slot(x, 'thickness'))
## setGeneric(name='on', def=function(x) standardGeneric('on'))
## setMethod(f='on', signature='TurtleWithPen', function(x) slot(x, 'on'))
## setGeneric(name='colour<-', def=function(x, value) standardGeneric('colour<-'))
## setMethod(f='colour<-', signature='TurtleWithPen', function(x, value) {slot(x, 'colour') = value; x})
## setGeneric(name='thickness<-', def=function(x, value) standardGeneric('thickness<-'))
## setMethod(f='thickness<-', signature='TurtleWithPen', function(x, value) {slot(x, 'thickness') = value; x})
## setGeneric(name='on<-', def=function(x, value) standardGeneric('on<-'))
## setMethod(f='on<-', signature='TurtleWithPen', function(x, value) {slot(x, 'on') = value; x})
We can now source that code to allow composition of methods for TurtleWithPen.
There is something unsavory about this approach to code generation. I produced it in the face of a task involving a configuration object that seems to require many slots. I wanted to avoid the temptation to use simple functions and the at sign. My hope is that programs along the lines of those demonstrated here will help lower the barrier to adoption of formal class and method definitions in R programming. There are surely many improvements to be made in what has been proposed here.