Is it bad practice to access S4 objects slots directly using @?
In general it is good programming practice to separate the content of an object from the interface, see this wikipedia article. The idea is to have the interface separate from the implementation, in that way the implementation can change considerably, without affecting any of the code that interfaces with that code, e.g. your script. Therefore, using @
creates less robust code that is less likely to work in a few years time. For example in the sp
-package mentioned by @mdsummer the implementation of how polygons are stored might change because of speed or progressing knowledge. Using @
, your code breaks down, using the interface your code works still. Except ofcourse if the interface also changes. But changes to the implementation are much more likely than interface changes.
Accessing and modifying arbitrarily deep nested S4 slots
Still happy to hear if anyone has thoughts. In the meantime, I've been unable to find a way to dynamically access and modify deep recursive layers in an S4 object. Rather, the best solution was to recursively collapse the object into a list of layers.
Due to the memory overhead of collapsing layers in a class-specific as.list
operation, I've decided to go for a list of objects while enforcing relationships between consecutive layers.
Accessing slots of an S4 function superclass
Resorting to a little language manipulation
setClass("Pow", representation("function", pow="numeric"),
prototype=prototype(
function(x) {
self <- eval(match.call()[[1]])
x^self@pow
}, pow=2))
and then
> f = g = new("Pow")
> g@pow = 3
> f(2)
[1] 4
> g(2)
[1] 8
although as Spacedman says things can go wrong
> f <- function() { Sys.sleep(2); new("Pow") }
> system.time(f()(2))
user system elapsed
0.002 0.000 4.005
A little more within the lines but deviating from the problem specification and probably no less easy on the eyes is
setClass("ParameterizedFunFactory",
representation(fun="function", param="numeric"),
prototype=prototype(
fun=function(x, param) function(x) x^param,
param=2))
setGeneric("fun", function(x) standardGeneric("fun"))
setMethod(fun, "ParameterizedFunFactory",
function(x) x@fun(x, x@param))
with
> f = g = new("ParameterizedFunFactory")
> g@param = 3
> fun(f)(2)
[1] 4
> fun(g)(2)
[1] 8
R S4 slots as lists of custom classes
For those looking for an answer to this. No, there is no way to restrict a list in S4 to have elements of certain type. This actually makes sense, since lists in R are designed to contain any type of element, so why lists on S4 should be distinct?
slot assignment: `@` vs `slot()` vs `setReplaceMethod()`
By definition, "not having to fuss with the details of the S4 class structure" means end-users should not have to know about your slots. As such, any wrappers you write as in steps 2 and 3 are more for internal consistency checks. I think more important is to check for edge cases where you think your integrity checks would fail using unit tests.
As you have pointed out, #1 can be ruled out fairly easily, and should only be used in internal methods. Whether you encourage #2 or implement #3 depends on the contents of the variable and personal taste, but I would encourage the latter. For example, if you have a logical
flag, you can use #2, but more descriptive would be enableFoo()
. That being said, if you have to consider developer time (which in real life is almost always true) you should think briefly about the trade-off between relegating mutators to slot<-
for members that probably won't be accessed frequently (e.g., by less than 1% of users), versus implementing custom mutators for everything as in #3.
Finally, since almost all three of R's OOP systems are essentially syntactic sugar and aren't really respected semantically by the language (where only S4 can claim some exception), it is easy to forget the fundamental ideas behind object-oriented programming as implemented in other languages: any code executing outside of your methods should not know about the object's members. You are providing an external interface to the world that is and should be a black box.
Related Topics
R: Arranging Multiple Plots Together Using Gridextra
Extract Nested List Elements Using Bracketed Numbers and Names
Regarding SQLdf Package/Regexp Function
Unique Elements of Two Vectors
Evaluate (I.E., Predict) a Smoothing Spline Outside R
How to Make a Post Request with Header and Data Options in R Using Httr::Post
Gathering Wide Columns into Multiple Long Columns Using Pivot_Longer
R Formatting a Date from a Character Mmm Dd, Yyyy to Class Date
Hashtag Extract Function in R Programming
Confidence Intervals for Predictions from Logistic Regression
Screening (Multi)Collinearity in a Regression Model
What Are Some Good Books, Web Resources, and Projects for Learning R
Delete Rows with Blank Values in One Particular Column