···39394040S7 marries (most of) the flexibility of S3 with (most of) the formality of S4. I find it strikes a perfect middle ground which makes it easier to dictate and understand what objects of type `<T>` can and can't do. S7 is elegantly formal. I first encountered S3 when speaking to Dr Bodwin; the topic came up somehow and she showed me how simple and flexible S3 is with an example, something like:
41414242-```{r}
4242+```r
4343x <- 1:10
4444class(x) <- "test"
4545mean.test <- function(x){
···52525353I was shocked at how easy it was to defined a class and write its method for a generic, especially since my knowledge of OOP was mostly in Java (which is very verbose). The flexibility offers a lot of obvious benefits, but it does come with some drawbacks. Firstly, I think it can be a bit hard to understand; I still find S3 object construction a bit strange. For example, if I wanted to skip `x`'s declaration, I could've written instead:
54545555-```{r}
5555+```r
5656mean.test <- function(x){
5757 "HELLO WORLD"
5858}
···64646565S7 may be formal, but it is also elegant–there are nice design patterns that can be implemented effortlessly. Take, for example, this implementation of some `Shape`s.
66666767-```{r}
6767+```r
6868library(S7)
69697070Shape <- new_class("Shape", abstract = TRUE)
···97979898It can easily be extended to admit a `Square` class:
9999100100-```{r}
100100+```r
101101Square <- new_class("Square",
102102 Rect,
103103 constructor = function(side) {
···111111112112And, more usefully, our classes can be (minimally) changed to guarantee that the provided values are positive–being numeric is already ensured by using `class_numeric`, even in the original definitions.
113113114114-```{r}
114114+```r
115115positive_numeric <- new_property(class_numeric,
116116 validator = function(value) {
117117 if (value <= 0) "must be greater than 0"
···150150151151We could also compute area in a property instead of a method:
152152153153-```{r}
153153+```r
154154positive_numeric <- new_property(class_numeric,
155155 validator = function(value) {
156156 if (value <= 0) "must be greater than 0"
···217217218218You can set read only properties with S7, which you can observe in the package (properties in ALLCAPS are constants or "final".) That is a very nice way to expose values which you wish users to be aware of but don't want to mess with, like the value of a linked list node.
219219220220-```{r}
220220+```r
221221# https://rconsortium.github.io/S7/articles/classes-objects.html#frozen-properties
222222eg <- new_class("eg",
223223 properties = list(
···243243244244You can, of course, bypass this:
245245246246-```{r}
246246+```r
247247attr(tmp, "VALUE") <- 1
248248tmp
249249#> <eg>
···258258259259I don't think you can't easily do anything like this in S7 ([yet](https://github.com/RConsortium/S7/issues/515)):
260260261261-```{r}
261261+```r
262262function(...){
263263 structure(..., class = "test")
264264}