On 08/23/2011 03:02 PM, Janko Thyson wrote:
Dear list,
I was wondering how to best implement some sort of a "plugin" paradigm
using R methods and the dispatcher:
Say we have a function/method ('foo') that does something useful, but
that should be open for extension in ONE specific area by OTHERS using
my package. Of course they could go ahead and write a whole new 'foo'
One possibility is to write class / method pairs. The classes extend
'Plugin', and the methods are on generic 'plug', with the infrastructure
## Approach 1: class / method pairs
setClass("Plugin")
setClass("DefaultPlugin", contains="Plugin")
DefaultPlugin <- function() new("DefaultPlugin")
setGeneric("plug",
function(plugin, src) standardGeneric("plug"),
signature="plugin",
valueClass="character")
setMethod(plug, "Plugin", function(plugin, src) {
src
})
foo <- function(src, plugin=DefaultPlugin()) {
plug(plugin, src)
}
This is extended by writing class / method pairs
setClass("Punct", contains="Plugin")
Punct <- function() new("Punct")
setMethod(plug, "Punct", function(plugin, src) {
gsub("[[:punct:]]", "", src)
})
setClass("Digit", contains="Plugin")
Digit <- function() new("Digit")
setMethod(plug, "Digit", function(plugin, src) {
gsub("[[:digit:]]", "", src)
})
The classes could have slots with state, accessible within the method.
An easier-on-the-user approach might have the Plugin class contain or
have slots of class "function". The user would only be obliged to
provide an appropriate function.
## Approach 2:
setClass("Plugin", prototype=prototype(function(src) {
src
}), contains="function")
Plugin <- function() new("Plugin")
setGeneric("foo",
function(src, plugin) standardGeneric("foo"))
setMethod(foo, c("character", "missing"),
function(src, plugin) foo(src, Plugin()))
setMethod(foo, c("character", "Plugin"),
function(src, plugin) plugin(src))
## 'Developer' classes
setClass("Punct", prototype=prototype(function(src) {
gsub("[[:punct:]]", "", src)
}), contains="Plugin")
Punct <- function() new("Punct")
setClass("Digit", prototype=prototype(function(src) {
gsub("[[:digit:]]", "", src)
}), contains="Plugin")
Digit <- function() new("Digit")
## General-purpose 'user' class
setClass("User", contains="Plugin")
User <- function(fun) new("User", fun)
This could have syntax checking in the validity method to catch some
mistakes early. In the S3 world, this is the approach taken by glm for
its 'family' argument, for instance str(gaussian().
Martin
method including the features they'd like to see, but that's not really
necessary. Rather, they should be able to just write a new "plugin"
method for that part of 'foo' that I'd like to open for such plugins.
The way I chose below works, but generates warnings as my method has
signature arguments that don't correspond to formal classes (which is
totally fine). Of course I could go ahead and make sure that such
"dummy" classes exist, but I was wondering if there's a better way.
It'd be great if anyone could let me know how they handle "plugin"
scenarios based on some sort of method dispatch!
Thanks,
Janko
##### CODE EXAMPLE #####
setGeneric(name="foo", signature=c("src"), function(src, ...)
standardGeneric("foo"))
setGeneric(name="plugin", signature=c("src", "link", "plugin"),
function(src, link, plugin, ...) standardGeneric("plugin")
)
setMethod(f="plugin", signature=signature(src="character", link="foo",
plugin="punct"),
function(src, link, plugin, ...){
out <- gsub("[[:punct:]]", "", src)
return(out)
}
)
setMethod(f="plugin", signature=signature(src="character", link="foo",
plugin="digit"),
function(src, link, plugin, ...){
out <- gsub("[[:digit:]]", "", src)
return(out)
}
)
setMethod(f="foo", signature=signature(src="character"),
function(src, plugin=NULL, ...){
if(!is.null(plugin)){
if(!existsMethod(f="plugin",
signature=c(src=class(src), link="foo", plugin=plugin)
)){
stop("Invalid plugin")
}
.plugin <- selectMethod(
"plugin",
signature=c(src=class(src), link="foo", plugin=plugin),
useInherited=c(src=TRUE, plugin=FALSE)
)
out <- .plugin(src=src)
} else {
out <- paste("Hello world: ", src, sep="")
}
return(out)
}
)
foo(src="Teststring:-1234_56/")
foo(src="Teststring:-1234_56/", plugin="punct")
foo(src="Teststring:-1234_56/", plugin="digit")
______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
--
Computational Biology
Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N. PO Box 19024 Seattle, WA 98109
Location: M1-B861
Telephone: 206 667-2793
______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel