How to create a virtual reference class in R?
Asked Answered
B

2

10

I can't find much on virtual/abstract classes in help(ReferenceClasses) - can anyone provide a basic example in creating one? Moreover, how can I specify a virtual method and enforce that child classes must implement it?

Bitterweed answered 15/1, 2014 at 16:24 Comment(0)
T
8

Reference classes are S4 classes. SO maybe you should see the help of setClass and Classes:

Here a dummy example:

# virtual Base Class
setRefClass( 
  Class="virtC", 
  fields=list( 
    .elt="ANY" 
  ),
  methods = list(
    .method = function(){
      print("Base virtual method is called")
    }
  ), 
  contains=c("VIRTUAL") 
) 
   
## child 1
## field as numeric and base .method is used
setRefClass( 
  Class="childNum", 
  fields=list( 
    .elt="numeric" 
  ), 
  contains=c("virtC")  
)


## child 2 
## field is char and .method is overwritten
setRefClass( 
  Class="childChar", 
  fields=list( 
    .elt="character" 
  ), 
  methods = list(
    .method = function(){print('child method is called')}
  ), 
  contains=c("virtC") 
) 
##  new('virtA')          ## thros an error can't isntantiate it
a = new("childChar",.elt="a")
b =   new("childNum",.elt=1)

b$.method()
[1] "Base virtual method is called"

a$.method()
[1] "child method is called"
Treasurer answered 15/1, 2014 at 16:50 Comment(4)
Thanks, but while explicit instantiation of the virtual class virtC is caught, there appears to be no mechanism for ensuring that child classes actually implement all abstract methods inherited from the parent -- I am still able to declare class childChar without having implemented $.method. Any way to correct this behaviour?Bitterweed
@MiloChen thats because R simply does not know about abstract methods. If you'd want to force that .method is overwritten, you could implement it in the virtual class with stop( "virtC$.method() must be implemented" ).Nathalie
@MiloChen I just add that Class are virtual and not abstract. I edit my example to show this feature.Treasurer
I was afraid of that. I'd sincerely hoped for some mechanism to check for poorly-defined classes during their definition rather than relying on raising runtime errors. I mean, they've implemented checks for local assignment to field names (field <- var instead of field <<- var) ...Bitterweed
B
1

This is an approach I am thinking of taking to catch unimplemented methods from inherited VIRTUAL "interfaces". It doesn't catch an incorrect implementation statically, but it does error if you try to instantiate an object that doesn't implement all of an interfaces methods. Rather than wait for a runtime error when invoking an unimplemented function, this bombs as soon as you try to create an object.

Helper Functions

Helper function to search the inheritance tree for interfaces. This is too basic right now. What I need to do is crawl the tree and check for parents that inherit the Interface class themselves...

.get_interface_methods <- function(.self) {

  ## Get the environment of the class being instantiated
  env <- attributes(.self$.refClassDef)$refMethods

  ## get original interface methods
  supers <- selectSuperClasses(class(.self))
  methods <- unlist(lapply(supers, function(x) getRefClass(x)$methods()))

  ## check the body is NOT null in the concrete class environment
  funs <- Filter(is.function, lapply(methods, get, envir=env))
  null_fun_body <- vapply(Map(body, funs), is.null, T)

  ## return names of functions not implemented
  vapply(funs[null_fun_body], attr, "", which="name")
}

Another helper function that is called when instantiating an object that contains one or more interfaces.

.validate_interface <- function(.self) {

  methods <- get_interface_methods(.self)

  ## stop the world and print out the un-implemented methods
  if (length(methods) > 0L) {
    stop("Must implement interface methods: ", paste(methods, collapse = ", "))
  }
}

The Classes

The Interface class simply calls the validate function during initialization. A class that inherits from Interface can register interface methods using any function that has a NULL body. I created a simple helper for that.

setRefClass(
  "Interface",
  methods = list(
  initialize = function() {
    validate(.self)
  }), contains="VIRTUAL")

InterfaceMethod <- function() NULL

Interfaces

Here I create two interfaces with dummy methods that have NULL bodies.

## Create an interface to be implemented
setRefClass(
  "ITest1",
  contains=c("VIRTUAL", "Interface"),
  methods = list(
    foo = InterfaceMethod,
    bar = InterfaceMethod,
    baz = InterfaceMethod
))

## create a second interface
setRefClass(
  "ITest2",
  contains=c("VIRTUAL", "Interface"),
  methods = list(
    spam = InterfaceMethod,
    ham  = InterfaceMethod
  ))

Instantiating an Object

Finally, I create a class definition that is not virtual and that inherits both of my defined interfaces. In the definition, I implement two of the interface functions foo & baz but do not implement bar, spam, or ham:

Obj <- setRefClass(
  "Obj",
  contains = c("ITest1", "ITest2"),
  methods = list(
    foo = function() "Implemented!",
    baz = function() "Implemented!"
))

When I try to instantiate this object, I get an error.

> x <- Obj$new()
 Error in validate(.self) : 
  Must implement interface methods: bar, ham, spam
Biogen answered 12/9, 2018 at 21:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.