Functional interfaces for reference classes

146 views Asked by At

I am trying to figure out the conceptual implications of providing a functional user interface to reference classes (or indeed S4/S3 classes). In short, I am concerned that if I write code that looks like as below, then it is equivalent to writing functional code.

Here is a simple linear regression class:

linRegClass = setRefClass('linRegClass',
                          fields = list(formulaReg = 'formula',
                                        dataReg = 'data.frame'),
                          methods = list(doReg = function() {
                            lm(.self$formulaReg, data = .self$dataReg)
                          }))

linRegInstance = linRegClass$new(dataReg = cars, 
                    formulaReg = as.formula(speed ~ dist))
linRegInstance$doReg()
class(linRegInstance)

The object-oriented interface is not very user friendly, so as in Martin Morgan's slides, I will write a functional user interface for the underlying reference class:

fnLinReg = function(formulaReg, dataReg) {
  linRegInstance = linRegClass$new(formulaReg = formulaReg, 
                                   dataReg = dataReg)
  linRegInstance$doReg()
}

## use the functional interface
fnLinReg(dataReg = cars, formulaReg = as.formula(speed ~ dist))

Now this functional interface is observationally equivalent to a purely functional

fnLinReg2 = function(formulaReg, dataReg) {
  lm(formula = formulaReg, data = dataReg)
}

## use the pure function
fnLinReg2(dataReg = cars, formulaReg = as.formula(speed ~ dist))

I am trying to figure out whether this is because my example is pathologically simple, but I still wonder if there is any point in writing reference classes and then wrapping them in functional interfaces versus writing purely functional code.

Any detailed examples would help greatly.

1

There are 1 answers

9
Scott Ritchie On BEST ANSWER

Complicated constructors can be defined using the initialize method, which will automatically be called when you call new. I've modified your example to contain the initialize method, and a new field to store the regression result, because initialize will always return the referenceClass:

linRegClass = setRefClass('linRegClass',
                          fields = list(formulaReg = 'formula',
                                        dataReg = 'data.frame',
                                        result = 'lm'),
                          methods = list(doReg = function() {
                            lm(.self$formulaReg, data = .self$dataReg)
                          },
                          initialize = function(formulaReg, dataReg, ...) {
                            formulaReg <<- formulaReg
                            dataReg <<- dataReg
                            result <<- .self$doReg()
                          }))

So now we simply have:

linRegInstance <- linRegClass$new(dataReg = cars, 
                                    formulaReg = as.formula(speed ~ dist))
linRegInstance$result