bookclub-advr

DSLC Advanced R Book Club
git clone https://git.eamoncaddigan.net/bookclub-advr.git
Log | Files | Refs | README | LICENSE

commit 29e2e1bc90c707eb18fe6dd4c8ca7edce64d0fdc
parent c2e48aaa8c37ee29c73b89ccb6945a7774d12a56
Author: DrEntropy <DrEntropy@users.noreply.github.com>
Date:   Tue, 11 Apr 2023 05:31:51 -0700

Chapter 16 Notes Edited for Cohort  7 (#49)

* Adding S7 notes

* Partial draft

* Remove review of oop

* First complete draft

* typos fixed
Diffstat:
M16_Trade-offs.Rmd | 586+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mbookclub-advr.Rproj | 36++++++++++++++++++------------------
2 files changed, 398 insertions(+), 224 deletions(-)

diff --git a/16_Trade-offs.Rmd b/16_Trade-offs.Rmd @@ -1,206 +1,380 @@ -# Trade-offs - -**Learning objectives:** - -- Understand the Trade-offs between S3, R6 and S4 - - -## Introduction to trade-offs - -> “Everything that exists in R is an object” -> -> -- <cite>John Chambers</cite> - -R is a functional language, **functions** made in R are **objects** with special **attributes**, called **classes**. - -The **object-oriented system (OOP)** is the **environment** where objects and classes are defined and operate with a **scope**. - -The **inheritance** concept in OOP allows one class to derive the features and functionalities of another class. This feature facilitates code-reusability. - -```{r 16-Trade-offs-1, echo=FALSE,fig.align='center', fig.cap="[OOP](https://www.javatpoint.com/r-object-oriented-programming#:~:text=What%20is%20Object%2DOriented%20Programming,do%20programming%20in%20oops%20style.)"} -knitr::include_graphics("images/16-oop.png") -``` - -**What is an object?** - -An object is a **data structure** that contains some **methods** based on attributes. -```{r} -sloop::otype(1:10) -sloop::otype(mtcars) -``` - - -An object is also called an **instance** of a class, and the process of object creation is called **instantiation**.[^1] - -[^1]: [R – Object Oriented Programming](https://www.geeksforgeeks.org/r-object-oriented-programming/) -```{r} -class(1:10) -class(mtcars) -``` - - -**What is a class?** - -A class is where objects are defined and obtained by **encapsulating** data and functions. - - -There are different types of classes in OOP: - -- **S3** -- **R6** -- **S4** - - -```{r 16-Trade-offs-2, message=FALSE, warning=FALSE, paged.print=FALSE,echo=FALSE} -library(tidyverse) -``` - - - -```{r 16-Trade-offs-3, echo=FALSE,fig.align='center',fig.cap="[r object-oriented programming](https://www.geeksforgeeks.org/r-object-oriented-programming/)"} -knitr::include_graphics("images/16-objects.png") -``` - - -## Understanding the Trade-offs - -Here we look to compare and contrast the OOP objects. - - -### S4 versus S3 - -**Which class to use S4 or S3? ** - -- **S3** is a flexible class, made of a list with attributes and assigned names, it consists of three main **components**: - - - Generic function - - method - - attributes - - - -- **S4** has a more structured approach, is more formal, more strict, and more verbose. It contains functions for defining methods and **generics**. - -For example to define a class: - - S3: class() <- ... - S4: setClass() - -**S4 is a combination of increased complexity**, as well as S3, it allows for inheritance of both classes and methods. - -S4 is usually good for larger projects, such as **Bioconductor**, and complex systems of interrelated objects: - -It minimizes code duplication (an example is the {Matrix} package), and makes it easy to provide a general method that works for all inputs. - - - -### R6 versus S3 - -When starting with OOP the S3 class is suggested as a default for its simplicity. Then, the tendency would be to lean towards **R6**. - -Example of S3: - - plot(data) - -Example of R6: - - data$plot - -**R6** is built on **encapsulated objects**, rather than generic functions. - - -**Big differences: general trade-offs** - -```{r 16-Trade-offs-4, echo=FALSE,fig.align='center'} -knitr::include_graphics("images/16-trade-offs.png") -``` - -#### Namespacing - -**Where methods are in the Space?** - -- in S3, **Generic functions** are **global**: there is a **global namespace** (same verbs, uniform API). The negatives are related to **homonyms** methods. - -- in R6, **Encapsulated methods** are **local**: objects with a **scope** - - -#### Threading state or accumulator programming - - -In S3 the challenge is to return a value and modify the object. This can't be done, it violates set guidelines. - - -As an example, see the **16-example_accumulator_programming.R** in the scripts folder. - - -Or a way to do **multiple assign** operators with the {zeallot} package. - -```{r 16-Trade-offs-5, eval=FALSE} -vignette('unpacking-assignment') -``` - - -#### Method chaining - -Useful to compose functions from left-to-right. - -Use of the operators: - -- S3: `%>%` -- R6: `$` - - - - -## Meeting Videos - -### Cohort 1 - -`r knitr::include_url("https://www.youtube.com/embed/W1uc8HbyZvI")` - -### Cohort 2 - -`r knitr::include_url("https://www.youtube.com/embed/bzo37PHCM1I")` - -### Cohort 3 - -`r knitr::include_url("https://www.youtube.com/embed/_byYFTQHp1Y")` - -### Cohort 4 - -`r knitr::include_url("https://www.youtube.com/embed/vdKDPBcOc6Y")` - -### Cohort 5 - -`r knitr::include_url("https://www.youtube.com/embed/3EvqtVYTFVM")` - -### Cohort 6 - -`r knitr::include_url("https://www.youtube.com/embed/vEButxFIvLw")` - -<details> -<summary> Meeting chat log </summary> - -``` -00:11:36 Oluwafemi Oyedele: I have not built anything with them!!! -00:16:31 Arthur Shaw: https://cran.r-project.org/web/packages/sp/index.html -00:19:05 Arthur Shaw: Apparently Hadley asked the same question we're asking several years ago: https://stackoverflow.com/questions/5437238/which-packages-make-good-use-of-s4-objects -00:19:16 Trevin: HA -00:23:54 Trevin: Your audio is breaking up Federica -01:06:58 Federica Gazzelloni: https://mastering-shiny.org/reactive-motivation.html?q=R6#event-driven -01:07:37 Federica Gazzelloni: https://engineering-shiny.org/common-app-caveats.html?q=R6#using-r6-as-data-storage -01:10:52 Oluwafemi Oyedele: Thank you !!! -``` -</details> - -### Cohort 7 - -`r knitr::include_url("https://www.youtube.com/embed/URL")` - -<details> - -<summary>Meeting chat log</summary> -``` -LOG -``` -</details> +# Trade-offs + +**Learning objectives:** + +- Understand the Trade-offs between S3, R6 and S4 + +- Brief intro to S7 (the object system formerly known as R7) + + +## Introduction + +* We have three OOP systems introduced so far (S3, S4, R6) + +* At the current time (pre - S7?) Hadley recommends use S3 by default: It's simple and widely used throughout base R and CRAN. + +* If you have experience in other languages, *Resist* the temptation to use R6 even though it will feel more familiar! + + +## S4 versus S3 + +**Which functional object system to use, S4 or S3? ** + +- **S3** is a simple and flexible system. + + - Good for small teams who need flexibility and immediate payoffs. + + - Commonly used throughout base R and CRAN + + - Flexibility can cause problems, more complex systems might require formal conventions + + +- **S4** is a more formal, strict system. + + - Good for large projects and large teams + + - Used by Bioconductor project + + - Requires significant up front investment in design, but payoff is a robust system that enforces conventions. + + - S4 Documentation is challenging to use. + + + +## R6 versus S3 + +**R6** is built on **encapsulated objects**, rather than generic functions. + + +**Big differences: general trade-offs** + +```{r 16-Trade-offs-4, echo=FALSE,fig.align='center'} +knitr::include_graphics("images/16-trade-offs.png") +``` + +## Namespacing {-} + +**Where methods are found?** + +- in S3, **Generic functions** are **global** and live in the **global namespace** + + - Advantage: Uniform API: `summary`, `print`, `predict` etc. + + - Disadvantage: Must be careful about creating new methods! Homonyms must be avoided, don't define `plot(bank_heist)` + + +- in R6, **Encapsulated methods** are **local**: objects with a **scope** + + - Advantage: No problems with homonyms: meaning of `bank_heist$plot()` is clear and unambiguous. + + - Disadvantage: Lack of a uniform API, except by convention. + + +## Threading state {-} + + +In S3 the challenge is to return a value and modify the object. + + +```{r} +new_stack <- function(items = list()) { + structure(list(items = items), class = "stack") +} + +push <- function(x, y) { + x$items <- c(x$items, list(y)) + x +} +``` + +No problem with that, but what about when we want to pop a value? We need to return two things. + +```{r} +pop <- function(x) { + n <- length(x$items) + + item <- x$items[[n]] + x$items <- x$items[-n] + + list(item = item, x = x) +} +``` + +The usage is a bit awkward: + +```{r} +s <- new_stack() +s <- push(s, 10) +s <- push(s, 20) + +out <- pop(s) +# Update state: +s <- out$x + +print(out$item) +``` + + +In python and other languages we have structured binding to make this less awkward. R has the {zeallot} package. For more, see this vignette: + +```{r 16-Trade-offs-5, eval=FALSE} +vignette('unpacking-assignment') +``` + +However, this is all easier in R6 due to the reference semantics! + +```{r} +Stack <- R6::R6Class("Stack", list( + items = list(), + push = function(x) { + self$items <- c(self$items, x) + invisible(self) + }, + pop = function() { + item <- self$items[[self$length()]] + self$items <- self$items[-self$length()] + item + }, + length = function() { + length(self$items) + } +)) + +s <- Stack$new() +s$push(10) +s$push(20) +s$pop() +``` + + +## Method chaining {-} + +Useful to compose functions from left-to-right. + +Use of the operators: + +- S3: `%>%` + +- R6: `$` + +```{r} +s$push(44)$push(32)$pop() +``` + + +## Umm... what about S7 ? {-} + +```{r standards, echo = FALSE, fig.cap = "https://xkcd.com/927/"} + +knitr::include_graphics("https://imgs.xkcd.com/comics/standards_2x.png") + +``` + +### Primary references: {-} + +* Docs: https://rconsortium.github.io/OOP-WG/ + +* Talk by Hadley Wickham https://www.youtube.com/watch?v=P3FxCvSueag + +## S7 briefly {-} + +* S7 is a 'better' version of S3 with some of the 'strictness' of S4. + +``` +"A little bit more complex then S3, with almost all of the features, +all of the payoff of S4" - rstudio conf 2022, Hadley Wickham +``` + +* Compatible with S3: S7 objects are S3 objects! Can even extend an S3 object with S7 + +* Somewhat compatible with S4, see [compatability vignette](https://rconsortium.github.io/OOP-WG/articles/compatibility.html) for details. + +* Helpful error messages! + +* Note that it was previously called R7, but it was changed to "S7" to better reflect that it is functional not encapsulated! + +## Abbreviated introduction based on the vignette {-} + +To install: +```{r, eval=FALSE} +# install.packages("remotes") +remotes::install_github("rconsortium/OOP-WG") +``` + + +```{r, eval=FALSE} +library(S7) +dog <- new_class("dog", properties = list( + name = class_character, + age = class_numeric +)) +dog + + +#> <S7_class> +#> @ name : dog +#> @ parent: <S7_object> +#> @ properties: +#> $ name: <character> +#> $ age : <integer> or <double> +``` + +Note the 'class_character', these are S7 classes corresponding to the base classes. + +Now to use it: + +```{r, eval = FALSE} +lola <- dog(name = "Lola", age = 11) +lola + +#> <dog> +#> @ name: chr "Lola" +#> @ age : num 11 +``` + +Properties can be set / read with '@', with automatic validation ('safety rails') based on the type! + +```{r, eval = FALSE} + +lola@age <- 12 +lola@age + +#> 12 + +lola@age <- "twelve" + +#> Error: <dog>@age must be <integer> or <double>, not <character> + +``` + +Note thehelpful error message! + +Like S3 (and S4) S7 has generics, implemented with `new_generic` and `method` for particular methods: + +```{r, eval = FALSE} +speak <- new_generic("speak", "x") + +method(speak, dog) <- function(x) { + "Woof" +} + +speak(lola) + +#> [1] "Woof" +``` + +If we have another class, we can implement the generic for that too: + +```{r, eval = FALSE} +cat <- new_class("cat", properties = list( + name = class_character, + age = class_double +)) +method(speak, cat) <- function(x) { + "Meow" +} + +fluffy <- cat(name = "Fluffy", age = 5) +speak(fluffy) + +#> [1] "Meow" +``` + +Helpful messages: + +```{r, eval = FALSE} +speak + +#> <S7_generic> speak(x, ...) with 2 methods: +#> 1: method(speak, cat) +#> 2: method(speak, dog) +``` + + +"most usage of S7 with S3 will just work" + +```{r, eval = FALSE} +method(print, cat) <- function(...) { + print("I am a cat.") +} + +print(fluffy) +#> "I am a cat" + +``` + +*For validators, inheritance, dynamic properties and more, see the [vignette!](https://rconsortium.github.io/OOP-WG/articles/S7.html)* + +https://rconsortium.github.io/OOP-WG/articles/S7.html + +## So... switch to S7 ? {-} + +$$ +\huge +\textbf{Soon}^{tm} +$$ + +* Not yet... still in development! + +* But consider trying it out: + + * To stay ahead of the curve... S7 will be integrated into base R someday! + + * To contribute feedback to the S7 team! + + * To get "almost all" of the benefits of S4 without the complexity ! + +* In particular, if you have a new project that might require the complexity of S4, consider S7 instead! + +## Meeting Videos + +### Cohort 1 + +`r knitr::include_url("https://www.youtube.com/embed/W1uc8HbyZvI")` + +### Cohort 2 + +`r knitr::include_url("https://www.youtube.com/embed/bzo37PHCM1I")` + +### Cohort 3 + +`r knitr::include_url("https://www.youtube.com/embed/_byYFTQHp1Y")` + +### Cohort 4 + +`r knitr::include_url("https://www.youtube.com/embed/vdKDPBcOc6Y")` + +### Cohort 5 + +`r knitr::include_url("https://www.youtube.com/embed/3EvqtVYTFVM")` + +### Cohort 6 + +`r knitr::include_url("https://www.youtube.com/embed/vEButxFIvLw")` + +<details> +<summary> Meeting chat log </summary> + +``` +00:11:36 Oluwafemi Oyedele: I have not built anything with them!!! +00:16:31 Arthur Shaw: https://cran.r-project.org/web/packages/sp/index.html +00:19:05 Arthur Shaw: Apparently Hadley asked the same question we're asking several years ago: https://stackoverflow.com/questions/5437238/which-packages-make-good-use-of-s4-objects +00:19:16 Trevin: HA +00:23:54 Trevin: Your audio is breaking up Federica +01:06:58 Federica Gazzelloni: https://mastering-shiny.org/reactive-motivation.html?q=R6#event-driven +01:07:37 Federica Gazzelloni: https://engineering-shiny.org/common-app-caveats.html?q=R6#using-r6-as-data-storage +01:10:52 Oluwafemi Oyedele: Thank you !!! +``` +</details> + +### Cohort 7 + +`r knitr::include_url("https://www.youtube.com/embed/URL")` + +<details> + +<summary>Meeting chat log</summary> +``` +LOG +``` +</details> diff --git a/bookclub-advr.Rproj b/bookclub-advr.Rproj @@ -1,18 +1,18 @@ -Version: 1.0 - -RestoreWorkspace: Default -SaveWorkspace: Default -AlwaysSaveHistory: Default - -EnableCodeIndexing: Yes -UseSpacesForTab: Yes -NumSpacesForTab: 2 -Encoding: UTF-8 - -RnwWeave: Sweave -LaTeX: pdfLaTeX - -AutoAppendNewline: Yes -StripTrailingWhitespace: Yes - -BuildType: Website +Version: 1.0 + +RestoreWorkspace: Default +SaveWorkspace: Default +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes + +BuildType: Website