S4 class constructor and validation - r

I present a short code to create a S4 class myclass and ensure that objects are created if they verify a condition given by a parameter param
setClass("myclass", slot = c(x = "numeric"))
#constructor
ValidmyClass<- function(object, param = 1)
{
if(object#x == param) return(TRUE)
else return("problem")
}
setValidity("myclass", ValidmyClass)
setMethod("initialize","myclass", function(.Object,...){
.Object <- callNextMethod()
validObject(.Object,...)
.Object
})
For which I get the following error message Error in substituteFunctionArgs(validity, "object", functionName = sprintf("validity method for class '%s'", :
trying to change the argument list of for validity method for class 'myclass' with 2 arguments to have arguments (object)
I understand the issue with the arguments but I cannot find a way to solve this. The document about setValidity mentions that the argument method should be "validity method; that is, either NULL or a function of one argument (object)". Hence from my understanding excluding more than one argument.
Nevertheless, the idea behind this example is that I want to be able to test the construction of a myclass object based on the value of an external given parameter. If more conditions were to be added, I would like enough flexibility so only the function ValidmyClass needs to be updated, without necessarily adding more slots.

The validity function has to have one argument named object. When I need to create one argument functions but really have more arguments or data to pass in I often fall back to using closures. Here the implementation of your ValidmyClass changes in that it will now return the actual validity function. The argument of the enclosing function is then the set of additional arguments you are interested in.
setClass("myclass", slot = c(x = "numeric"))
#constructor
ValidmyClass <- function(param) {
force(param)
function(object) {
if (object#x == param) TRUE
else "problem"
}
}
setValidity("myclass", ValidmyClass(1))
Also the validity function is called automatically on init; however not when the slot x is changed after the object is created.
setMethod("initialize", "myclass", function(.Object,...) {
.Object <- callNextMethod()
.Object
})
new("myclass", x = 2)
new("myclass", x = 1)
For more information on closures see adv-R. Although I think this answers your question, I do not see how this implementation is actually helpful. When you define your class, you basically also fix the additional parameters which the validity function knows about. If you have several classes for which you can abstract the validity function then I would use the closure. If you have one class with changing parameters at runtime, I would consider to add a slot to the class. If you do not want to alter the class definition you can add a slot of class list where you the can pass in an arbitrary number of values to test against.

Related

What is distinction between initialize method vs prototype in setClass to set default values in R S4?

In R S4 OOP, I'm aware of 2 ways so set default values for a particular class.
1.) Using prototype in setClass()
setClass("Person",
slots = c(
name = "character",
age = "numeric"
),
prototype = list(
name = "Jim",
age = 25
)
)
2.) Using initialize in setMethod()
setMethod("initialize", "Person",
function(.Object,
name,
age,
...) {
.Object#name <- "Jim"
.Object#age <- 25
validObject(.Object)
return(.Object)
}
)
assign("Person", Person, envir = .GlobalEnv)
Can anyone please elaborate on the distinction between these two i.e. why would we want to include a separate initialize method vs using prototype() in setClass()? What is S4 best practice?
Thank you in advance for the response.
The prototype is only able to handle simple cases; in particular the value of slots cannot depend on each other and the user cannot specify any dynamic values. For example, if you have a simple class
setClass("personInfo",
slots=list(
age="numeric",
ageSquared="numeric"
), prototype=prototype(
age=18,
ageSquared=18^2
) -> personInfo
then the prototype can only create 18-year old people. Instead, it is more useful to create an initialize method as a function of age, which can then set both age and ageSquared to the relevant values.
Also note that if you assign the results of setClass into an object (as I did in the example above, assigning it into an object called personInfo then personInfo becomes a call to initialize and you can now call e.g.
personInfo(age=10,ageSquared=100)
(though this isn't ideal in this case as you have to explicitly supply ageSquared when you could have had the method do that for you.)
I would recommend:
using a prototype for any slot that has a reasonable default value (every class/slot has an implicit prototype anyway)
always keeping the return value from setClass as an object
over-riding that with a method of initialize if some slots depend on others (like ageSquared here)

Converting object of class function into function

In R, assume an object of type 'closure' called my_object that contains both a likelihood function and the associated parameters.
Assume further that I would now like to extract specifically the likelihood function from said object and pass it on to a different R command, which needs a likelihood function as its first argument. I can extract said function via command unenclose in library pryr.
> library(pryr)
> lik_func = unenclose(my_object)
> lik_func
function (pars, condition.surv = TRUE, root = ROOT.OBS, root.p = NULL,
intermediates = FALSE)
{ #function_specifics_here# }
<environment: namespace:diversitree>
However, what I apparently extract is just an object of class 'function', and not the likelihood function itself, as the next R command complains that it does not actually receive a function, but an object:
> asr.marginal(lik=lik_func, pars=my_pars)
Error in UseMethod("make.asr.marginal") :
no applicable method for 'make.asr.marginal' applied to an object of class "function"
How do I convert this object of class 'function' into a plain function, assuming such a distinction actually exists?
Note: I am uncertain if or why a distinction between an object of class 'function' and a plain function actually exists. Maybe someone answering this question could share some light on this too.
“Objects of class "function"” are, generally1, functions. In particular, they are objects of type "closure" and class "function".
Using pryr::unenclose doesn’t have any useful effect here. Its only effect is to take a function and replace all references to objects in an enclosing environment with their value. So if I have, say:
x = 1
f = function () x
… then unenclose(f) will yield:
function () 1
This doesn’t make f any more or less of a function.
Your error message seems to be fundamentally unrelated to that. Instead, asr.marginal specifically expects a likelihood function, which apparently needs to be created by one of the make.* functions in the package. A likelihood function in the context of ‹diversitree› is a function of class "dtlik".
1 The exception is if you are prone to shenanigans:
x = 42
class(x) = "function"
Now x has class function but obviously isn’t a function.

Can we combine S3 flexibility with S4 representation checking?

I'm looking for a method to validate S3 objects in my package Momocs.
Earlier versions of the package were written using S4, then I shifted back to S3 for the sake of flexibility, because users were more into S3, because I do not really need multiple inheritance, etc.. The main cost of this change was actually losing S4 representation / validity checking.
My problem follows: how can we prevent one from inadvertently "unvalidate" an S3 object, for instance trying to extend existing methods or manipulating object structure?
I have already written some validate function but, so far, I only validate before crucial steps, typically those turning an object from a class into another.
My question is:
do I want to have my cake and eat it (S3 flexibility and S4 representation checking) ? In that case, I would need to add my validate function across all the methods of my package?
or is there a smarter way on top of S3, something like "any time we do something on an object of a particular class, call a validate function on it"?
The easiest thing would be to write a validation function for each class and pass objects through it before S3 method dispatch or within each class's method. Here's an example with a simple validation function called check_example_class for an object of class "example_class":
check_example_class <- function(x) {
stopifnot(length(x) == 2)
stopifnot("a" %in% names(x))
stopifnot("b" %in% names(x))
stopifnot(is.numeric(x$a))
stopifnot(is.character(x$b))
NULL
}
print.example_class <- function(x, ...) {
check_example_class(x)
cat("Example class object where b =", x$b, "\n")
invisible(x)
}
# an object of the class
good <- structure(list(a = 1, b = "foo"), class = "example_class")
# an object that pretends to be of the class
bad <- structure(1, class = "example_class")
print(good) # works
## Example class object where b = foo
print(bad) # fails
## Error: length(x) == 2 is not TRUE

Using a method/function within a reference class method of the same name

When defining a new reference class in R there is a bunch of boiler-plate methods that are expected (by R conventions), such as length, show etc. When these are defined they aggressively masks similar named methods/functions when called from within the class' methods. As you can not necessarily know the namespace of the foreign function it is not possible to use the package:: specifier.
Is there a way to tell a method to ignore its own methods unless called specifically using .self$?
Example:
tC <- setRefClass(
'testClass',
fields = list(data='list'),
methods = list(
length=function() {
length(data)
}
)
)
example <- tC(data=list(a=1, b=2, c=3))
example$length() # Will cause error as length is defined without arguments
Alternatively one could resort to defining S4 methods for the class instead (as reference classes are S4 classes under the hood), but this seems to be working against the reference class idea...
Edit:
To avoid focusing on instances where you know the class of the data in advance consider this example:
tC <- setRefClass(
'testClass',
fields = list(data='list'),
methods = list(
length=function() {
length(data)
},
combineLengths = function(otherObject) {
.self.length() + length(otherObject)
}
)
)
example <- tC(data=list(a=1, b=2, c=3))
example$combineLength(rep(1, 3)) # Will cause error as length is defined without arguments
I am aware that it is possible to write your own dispatching to the correct method/function, but this seems as such a common situation that I thought it might have already been solved within the methods package (sort of the reverse of usingMethods())
My question is thus, and I apologise if this wasn't clear before: Are there ways of ignoring there reference class methods and fields within the method definitions and solely rely on .self for accessing these, so that methods/functions defined outside the class are not masked?
The example is not very clear. I don't know for what reason you can't know the namespace of your method. Whatever, here a couple of methods to work around this problem:
You can use a different name for the reference class method Length with Capital "L" for example
You can find dynamically the namespace of the generic function.
For example:
methods = list(
.show =function(data) {
ns = sub(".*:","",getAnywhere("show")$where[1])
func = get("show",envir = getNamespace(ns))
func(data)
},
show=function() {
.show(data)
}
)
You can use the new reference class System R6.
For example:
tC6 <- R6Class('testClass',
public = list(
data=NA,
initialize = function(data) {
if (!missing(data)) self$data <- data
},
show=function() show(self$data)
)
)

Is 'show' a normal S4 generic function?

I'm trying to create a method for my class, which inherits from data.frame. I was originally hoping just to inherit the 'show' method from data.frame as well, but I'm also fine with writing my own. I defined my class and 'show' method as follows:
setClass("SCvec", representation(auth = "character",
dev = "character",
sensor = "character",
channel = "character",
starttime = "character",
endtime = "character"),
contains="data.frame")
setMethod("show", signature(x="SCvec"), function(x) print(x))
when I type show in the R console, it prints out:
standardGeneric for "show" defined from package "methods"
function (object)
standardGeneric("show")
<bytecode: 0x0396bee8>
<environment: 0x0393ab60>
Methods may be defined for arguments: object
Use showMethods("show") for currently available ones.
(This generic function excludes non-simple inheritance; see ?setIs)
So it sure looks like I don't need to turn it into a generic using StandardGeneric() myself. but when I run my setMethod("show", signature(x="SCvec"), function(x) print(x)) line, I get the error
Error in match.call(definition, call, expand.dots) :
unused argument(s) (x = c("SCvec", ""))
I've defined this method just like I would define any other one. Why does this method definition not work? Is 'show' different than other generic functions?
The function show takes an argument object, so you would need to define your signature and function definition with respect to that formal argument:
setMethod("show", signature(object="SCvec"), function(object) print(object))
You can also see other methods that are defined for show by typing in
showMethods(show)
And this shows you that all the other methods are defined in terms of the class of object as well.

Resources