A toy package designed to showcase and serve as a reference/tutorial for S7 development. To be used in conjunction with the S7 package webs
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Expanded Shape example

+55 -14
+55 -14
README.md
··· 30 30 31 31 Here's an unordered list of some nice things about S7. I won't directly address these ideas, but you can observe them throughout the code. 32 32 33 - 1. Just the right amount of formal 34 - 2. Just the right amount of flexible 33 + 1. Just the right amount of formality 34 + 2. Just the right amount of flexibility 35 35 3. Cogent API 36 36 4. Easy to extend 37 37 5. Classes, properties, validators, etc. give nice guarantees 38 + 6. Simple to swap to from S3 38 39 39 - 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: 40 + S7 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: 40 41 41 42 ```{r} 42 43 x <- 1:10 ··· 59 60 mean(structure(1:10, class = "test")) 60 61 ``` 61 62 62 - but I think this is harder to reason about. The idea of just giving a garden variety vector `1:10` a whole class without really defining what that class is, is strange to me. I find it hard sometimes to understand what things are. S7 makes it very clear what things are; I find this easier to reason about. This is something I noted firsthand when swapping to S7 in some Tidyverse packages. Even with well documented code, it can be hard to understand how things work. 63 + but I think this is harder to reason about. The idea of just giving a garden variety vector `1:10` a whole class without really defining what that class is, is strange to me. I find it hard sometimes to understand what S3 objects are and how they interact with methods. S7 makes it very clear what things are; I find this easier to reason about. This is something I noted firsthand when swapping to S7 in some Tidyverse packages. Even with well documented code, it can be hard to understand how things work. 63 64 64 65 S7 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. 65 66 ··· 86 87 It can easily be extended to admit a `Square` class: 87 88 88 89 ```{r} 89 - Square <- new_class("Square", Rect, constructor = function(side) new_object(Square, width = side, height = side)) 90 + Square <- new_class("Square", Rect, constructor = function(side) new_object(S7_object(), width = side, height = side)) 90 91 square <- Square(5) 91 92 92 93 Area(square) ··· 100 101 Circle <- new_class("Circle", Shape, properties = list(radius = positive_numeric)) 101 102 Rect <- new_class("Rect", Shape, properties = list(width = positive_numeric, height = positive_numeric)) 102 103 103 - # Rect(0, 4) 104 + Rect(0, 4) 104 105 #> ! <Rect> object properties are invalid: 105 106 #> - @width must be greater than 0 106 107 107 - # Circle(-10) 108 + Circle(-10) 108 109 #> ! <Circle> object properties are invalid: 109 110 #> - @radius must be greater than 0 110 111 111 112 # and of course this still holds for Squares 112 - # Square(-1) 113 + Square(-1) 113 114 #> ! <Square> object properties are invalid: 114 115 #> - @width must be greater than 0 115 116 #> - @height must be greater than 0 116 117 ``` 117 118 118 - This is hardly the most interesting example (hence the package!), and these aren't the most groundbreaking principles, but having these OOP ideas are very nice. They're presented in a cogent API and allow you to spell out what you need to do: `new_class()`, `new_generic()`, `new_external_generic()`, `method()`, etc. The formality and structure that S7 provides manifests as interpretable, clean code. Most importantly, it is readable code. 119 + We could also compute area in a property instead of a method: 120 + 121 + ```{r} 122 + positive_numeric <- new_property(class_numeric, validator = function(value) if (value <= 0) "must be greater than 0") 123 + 124 + Shape <- new_class("Shape", abstract = TRUE) 125 + Circle <- new_class( 126 + "Circle", 127 + Shape, 128 + properties = list(radius = positive_numeric, area = new_property(class_numeric, getter = function(self) pi * self@radius^2, setter = NULL)), 129 + constructor = function(radius) new_object(S7_object(), radius = radius) 130 + ) 131 + Rect <- new_class( 132 + "Rect", 133 + Shape, 134 + properties = list(width = positive_numeric, height = positive_numeric, area = new_property(class_numeric, getter = function(self) self@width * self@height, setter = NULL)), 135 + constructor = function(width, height) new_object(S7_object(), width = width, height = height) 136 + ) 137 + Square <- new_class("Square", Rect, constructor = function(side) new_object(S7_object(), width = side, height = side)) 138 + 139 + circ <- Circle(10) 140 + circ 141 + #> <Circle> 142 + #> @ radius: num 10 143 + #> @ area : num 314 144 + 145 + circ@area <- 10 146 + #> ! Can't set read-only property <Circle>@area 147 + 148 + Rect(10, 3) 149 + #> <Rect> 150 + #> @ width : num 10 151 + #> @ height: num 3 152 + #> @ area : num 30 153 + 154 + Square(5) 155 + #> <Square> 156 + #> @ width : num 5 157 + #> @ height: num 5 158 + #> @ area : num 25 159 + ``` 160 + 161 + `Shapes` are hardly the most interesting example (hence the package!), and these aren't the most groundbreaking principles, but having these OOP ideas in R is very nice. They're presented in a cogent API and allow you to spell out what you need to do: `new_class()`, `new_generic()`, `new_external_generic()`, `method()`, etc. This allows formality without being stuffy or verbose. The structure that S7 provides manifests as interpretable, clean code. Most importantly, it is very readable code. 119 162 120 163 #### Some Random Thoughts/Interesting Points 121 164 122 165 ##### Read Only Properties 123 166 124 - You can set read only properties with S7, which you can observe in the package (ALLCAPS properties 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. You can, of course, bypass this: 167 + You 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. You can, of course, bypass this: 125 168 126 169 ```{r} 127 170 # https://rconsortium.github.io/S7/articles/classes-objects.html#frozen-properties ··· 147 190 148 191 but you clearly have to go out of your way to. Normal usage of `eg` objects, with the exposed API, would ensure that you don't accidentally change constants. 149 192 150 - You can also do cool stuff with [computed/dynamic properties](https://rconsortium.github.io/S7/articles/classes-objects.html#properties). 193 + You can also do cool stuff with [computed/dynamic properties](https://rconsortium.github.io/S7/articles/classes-objects.html#properties). The last implementation of `Shape`s above uses this to compute the area dynamically–thus allowing the area to change as you adjust the measurements of a shape. 151 194 152 195 ##### Var Args 153 196 ··· 161 204 162 205 ##### State 163 206 164 - You have to use environments to get state in S7 (again, see package)--but I have it on very strong authority that a stateful S7 is being thought of. Don't quote me on that. 165 - 166 - S7 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 what objects of type `<T>` can and can't do. 207 + You have to use environments to get state in S7 (again, see package)--but I have it on strong authority that a stateful S7 is being thought of. Don't quote me on that. 167 208 168 209 ### BigNum 169 210