1

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.

tchakravarty
  • 10,736
  • 12
  • 72
  • 116
  • Your example very stilted. Any kind of OO is bad for this example: you're not storing the class and using its methods later, you simply want the call to `lm`, so of course the pure function will be better. – Scott Ritchie Dec 18 '13 at 06:45

1 Answers1

1

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
Scott Ritchie
  • 10,293
  • 3
  • 28
  • 64
  • less verbose variable names and class names also would help! – Scott Ritchie Dec 18 '13 at 06:42
  • Ha, I refuse to use less descriptive variable names. ;) Thanks for your answer. Could you give an example of a more elaborate example where interfacing to a reference class makes sense? – tchakravarty Dec 18 '13 at 06:50
  • It makes more sense to use it when you have an object, rather than a function. Like a particular data-type, or dataset (if you have a set of common analyses you want to perform). – Scott Ritchie Dec 18 '13 at 06:59
  • Ya, it is kinda dawning on me now. Thanks. – tchakravarty Dec 18 '13 at 06:59
  • 1
    By the way, you will run into [this issue](http://stackoverflow.com/q/18893290/1414455) with complex constructors with no defaults. – tchakravarty Dec 18 '13 at 07:14
  • There's two parts to that. One: `...` should be added as an extra argument to `initialize` for inheritance to work properly. I forgot that, so I've edited my answer. Two, in the linked question's case, they should really define an pseudo-abstract class that is parent to both. – Scott Ritchie Dec 18 '13 at 07:47
  • I actually have no idea if R can handle things like abstract classes or private methods. – Scott Ritchie Dec 18 '13 at 07:48
  • No private methods, that's for sure. – tchakravarty Dec 18 '13 at 07:51
  • I'm sure there's nothing stopping you from instantiating a class you've designed to be abstract either ;) – Scott Ritchie Dec 18 '13 at 10:03