From 63e8d397864c8b3b1761f18bfd99be3198cdce83 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 26 May 2025 14:40:22 +0200 Subject: [PATCH 1/5] add post --- .../figs/unnamed-chunk-11-1.png | Bin 0 -> 15914 bytes .../figs/unnamed-chunk-12-1.png | Bin 0 -> 15953 bytes .../figs/unnamed-chunk-3-1.png | Bin 0 -> 16314 bytes .../figs/unnamed-chunk-4-1.png | Bin 0 -> 16314 bytes content/blog/ggplot2-4-0-0-s7/index.Rmd | 335 ++++++++++++++++ content/blog/ggplot2-4-0-0-s7/index.md | 371 ++++++++++++++++++ .../blog/ggplot2-4-0-0-s7/thumbnail-sq.jpg | Bin 0 -> 20460 bytes .../blog/ggplot2-4-0-0-s7/thumbnail-wd.jpg | Bin 0 -> 21611 bytes 8 files changed, 706 insertions(+) create mode 100644 content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-11-1.png create mode 100644 content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-12-1.png create mode 100644 content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-3-1.png create mode 100644 content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-4-1.png create mode 100644 content/blog/ggplot2-4-0-0-s7/index.Rmd create mode 100644 content/blog/ggplot2-4-0-0-s7/index.md create mode 100644 content/blog/ggplot2-4-0-0-s7/thumbnail-sq.jpg create mode 100644 content/blog/ggplot2-4-0-0-s7/thumbnail-wd.jpg diff --git a/content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-11-1.png b/content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-11-1.png new file mode 100644 index 0000000000000000000000000000000000000000..77e7a6fe399feb9d3324d36c28f16a4da881a6cc GIT binary patch literal 15914 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 literal 0 HcmV?d00001 diff --git a/content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-12-1.png b/content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-12-1.png new file mode 100644 index 0000000000000000000000000000000000000000..5ee2429350a34627c208efa6b122464d0cf6d9c8 GIT binary patch literal 15953 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 literal 0 HcmV?d00001 diff --git a/content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-4-1.png b/content/blog/ggplot2-4-0-0-s7/figs/unnamed-chunk-4-1.png new file mode 100644 index 0000000000000000000000000000000000000000..1d411bc3588566ad9e3d30799a2d80c30eec8856 GIT binary patch literal 16314 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 literal 0 HcmV?d00001 diff --git a/content/blog/ggplot2-4-0-0-s7/index.Rmd b/content/blog/ggplot2-4-0-0-s7/index.Rmd new file mode 100644 index 000000000..47516cb69 --- /dev/null +++ b/content/blog/ggplot2-4-0-0-s7/index.Rmd @@ -0,0 +1,335 @@ +--- +output: hugodown::hugo_document + +slug: ggplot2-4-0-0-s7 +title: ggplot2 migrates to S7 +date: 2025-05-26 +author: Teun van den Brand +description: > + The ggplot2 package is migrating to S7 and we'd like to minimise any problems. + This guide details the classes and functions that ggplot2 has migrated and + how these might affect downstream packages. + +photo: + url: https://unsplash.com/photos/silhouette-of-person-standing-on-grass-field-during-sunset-Fr33DHTpLZk + author: Inhyeok Park + +# one of: "deep-dive", "learn", "package", "programming", "roundup", or "other" +categories: [package] +tags: [ggplot2, s7, package maintenance] +--- + + + +The ggplot2 package is on the verge to release version 4.0.0. +That is right: a new major version release! +We only tend to do these when something fundamental changes in ggplot2. +For example: ggplot2 2.0.0 brought the ggproto extension system and 3.0.0 switched to tidy evaluation. +This time around, we're swapping out the S3 object oriented programming system for the newer S7 system. +Because of this major change, we expect that some packages might break, despite our best efforts to minimise the damage. +This here is a guide for package authors that might be affected by this change. +This guide details some changes in classes and functions that may affect downstream packages, and gives recommendations how broken parts might be repaired. +If you don't maintain a package that depends on ggplot2, you can skip reading this guide and simply take away that there will be a release soon. + +## Testing compatibility + +If you are a package author that depends on ggplot2 and you want to know how your package might be affected, you can try the current development version from GitHub using the code below. + +```r +pak::pak("tidyverse/ggplot2") +``` + +It should also automatically install scales 1.4.0, which is needed for this release. +One of the things to inspect first is the result of R CMD check on your package, with the development version of ggplot2 installed. +It can be invoked by `devtools::check()`. +This is also the check CRAN also runs on your package to keep tabs on if your package continues to work. +If you are lucky, this will happily report that there are no problems and you can stop reading this guide! +If you are unlucky, it will list errors and warnings associated with running your package. +It might be that your examples no longer work, test assumptions are no longer met or vignettes run amock. +If you use visual snapshots from the vdiffr package, you may certainly expect (mostly harmless) imperceptible changes. + +As you're still reading, I'm assuming there are problems to solve. +The next step is determining who should fix these problems. +We have tried to facilitate some backwards compatibility, but we also cannot anticipate every contingency. +If something is broken with classes, generics, methods or object oriented programming in general, this guide describes problems and remedies. +Because ggplot2 does not go back to S3, we hope that you will facilitate the migration to S7 in your code where appropriate. +If there are other issues that pop up that you think might be best repaired in ggplot2, you can post an issue in the [issue tracker](https://github.com/tidyverse/ggplot2/issues). + +That said, let's go through S7 a bit. [S7](https://rconsortium.github.io/S7/) is a newer object oriented programming system that is built on top of the older S3 system. +It was build by a collaboration of developers from different niches in the R community, ranging from R Core, to Bioconductor to the tidyverse. +It aims to succeed the simpler S3 and more complex S4 systems. +Aside from simply modernising ggplot2, the migration to S7 also enables features that are hard to implement in S3, such as double dispatch. +For years now, people have been asking for more control over how plots are declared at both sides of the `+` operator, which S7 will facilitate. + +## Classes + +The ggplot2 package uses a mixture of object oriented programming systems. +One of these systems is the ggproto system that powers the extension mechanism and remains unchanged. +The other system is S3 which has been supplanted by S7 in the recent ggplot2 update. +You might notice this from the new S7 class objects that ggplot2 defines, like `class_ggplot` or `class_theme`. + +```{r} +library(ggplot2) +class_ggplot +``` + +### Properties + +In prior incarnations, ggplot2 defined the ggplot class as a named list with the `"ggplot"` class attribute. +Classes in S7 are more formal than in S3 and have properties which can have restricted classes. +For example, in the ggplot class, the `data` property can be anything (because it will go through `fortify()` to become a data frame), the `facet` property must be the `Facet` ggproto class, and the `theme` property must be an S7 theme object. + +In contrast to S3, we cannot simply add new items to `ggplot` object ^[This still 'works' for backwards compatibility reasons, but it will be phased out in the future, so it should be avoided.]. +The way to add additional information to classes in S7 is to make a subclass with additional properties. +For example, if we want to add for example colour information to a new plot, we can do the following: + +```{r} +inked_ggplot <- S7::new_class( + name = "inked_ggplot", + parent = class_ggplot, + properties = list(ink = S7::class_character) +) +inked_ggplot +``` + +When you define a new class, the object you've assigned it to automatically becomes the class definition which comes with a free, standard constructor. +This means that we can start building new plots with our subclass right away. +Note that we haven't implemented any behaviour around the `ink` property (yet), so it will just print like a normal plot. + +```{r} +my_plot <- inked_ggplot(data = mpg, ink = "red") + + geom_point(aes(displ, hwy)) +my_plot +``` + +In contrast to S3, where you would change list-items by using `$`, in S7 you can use `@` to read and write properties. +So if we want to change the stored `ink` colour, we can use: + +```{r} +my_plot@ink <- "blue" +``` + +### Testing + +In S3, the recommended way to test for the class of an object is to use a testing function. +For example `is.factor()`; and if such a testing function doesn't exist: use `inherits()`. +In S7, it is still recommended to use dedicating testing functions. +However, if these are absent, you can use `S7::S7_inherits()`. +If we wanted to write a testing function for our new class, we can do that as follows: + +```{r} +is_inked_ggplot <- function(x) S7::S7_inherits(x, inked_ggplot) + +# Is not our class +is_inked_ggplot(ggplot()) + +# Is our class +is_inked_ggplot(my_plot) +``` + +### Overview + +To give an overview of ggplot2's S7 classes, we include the table below. +The table also lists the recommended way to test for the class. + +```{r, echo=FALSE} +cls <- tibble::tribble( + ~`Old S3 Class`, ~`New S7 Class`, ~`Testing functions`, + '"ggplot2"', "class_ggplot", "`is_ggplot(x)`", + '"ggplot_built"', "class_ggplot_built", "`S7::S7_inherits(x, class_ggplot_built)`", + '"labs"', "class_labels", "`S7::S7_inherits(x, class_labels)`", + '"uneval"', "class_mapping", "`is_mapping(x)`", + '"theme"', "class_theme", "`is_theme(x)`", + '"element_blank"', "element_blank", '`is_theme_element(x, "blank")`', + '"element_line"', "element_line", '`is_theme_element(x, "line")`', + '"element_rect"', "element_rect", '`is_theme_element(x, "rect")`', + '"element_text"', "element_text", '`is_theme_element(x, "text")`', + NA, "element_polygon", '`is_theme_element(x, "polygon")`', + NA, "element_point", '`is_theme_element(x, "point")`', + NA, "element_geom", '`is_theme_element(x, "geom")`' +) +knitr::kable(cls) +``` + +### Testing + +It should be noted that the `is_*()` testing functions in ggplot2 already know about the S7-ness of the new classes. +This is handy when it comes to test expectations, because the testing function can be used instead of the S3/S7 class expectations. +Previously, you might have used `testthat::expect_s3_class()`, but it is better now to test with `testthat::expect_s7_class()` or use an `is_*()` function instead. + +```{r} +testthat::test_that( + "the plot object has the ggplot class", + { + plot <- ggplot() + + # Works regardless of S3 or S7 + testthat::expect_true(is_ggplot(plot)) + + # This will become dysfunctional in the future. + # Do not use this! + testthat::expect_s3_class(plot, "ggplot") + + # This will work in the new version + testthat::expect_s7_class(plot, class_ggplot) + } +) +``` + +The ggplot2 package manually appends the `"ggplot"` class for backwards compatibility reasons (likewise for `"theme"`). +However, once this phases out, the `testthat::expect_s3_class()` expectation will become untenable. +It is also currently flawed, as it does not work for subclasses! + +```{r, error=TRUE} +testthat::test_that( + "the inked plot has the ggplot class", + { + plot <- inked_ggplot() + testthat::expect_s3_class(plot, "ggplot") + } +) +``` + +The advice herein is thus to use `is_ggplot()`. + +## Generics and methods + +If you are new to object oriented programming in R, you might be unfamiliar what the terms 'generic' and 'methods' mean. +They are a form of 'polymorphism', where we can use a single function, called the 'generic' function, with different implementations for different classes (where one such implementation is called a 'method'). +A well known generic is `print()`, which does different things for different classes. +For example `print(1:10)` prints the numeric vector to the console, but `print(my_plot)` opens a graphics device to render the plot. + +### Your methods for ggplot's generics + +The ggplot2 package also declares some generic functions and contains methods for these, most of which revolve around plot construction. +The migration to S7 means that the generics and methods defined by ggplot2 also migrate. +While it is possible to define S7 methods for S3 generics, it is not possible to define S3 methods for S7 generics. + +```{r, error=TRUE} +# Declare an S7 generic +apply_ink <- S7::new_generic("apply_ink", "plot") + +# Attempt to implement an S3 method +apply_ink.inked_ggplot <- function(plot, ...) { + # Edit plot to our liking + plot@theme <- theme_gray(ink = plot@ink) + plot@theme + plot +} + +# Burn your fingers +apply_ink(my_plot) +``` + +To allow for a smoother transition from S3 to S7, we plan to keep S3 generics around for another release cycle but will permanently disable them in the future in favour of the S7 generics. +Here is an overview of which S7 generics supplant which S3 generics: + +```{r, echo=FALSE} +generics <- tibble::tribble( + ~`Old S3 Generic`, ~`New S7 Generic`, ~`Description`, + "`ggplot_add()`", "`update_ggplot()`", "Determines what happens when you `+` an object to a plot.", + "`ggplot_build()`", "`build_ggplot()`", "Processes data for display in a plot.", + "`ggplot_gtable()`", "`gtable_ggplot()`", "Renders a processed plot plot to a gtable object.", + "`element_grob()`", "`draw_element()`", "Renders a theme element." +) +knitr::kable(generics) +``` + +If your package implements methods for one of the old S3 generics, we recommend to replace these with S7 in a timely manner. +An important difference between S3 and S7 is that S7 does not use `NextMethod()` to magically invoke parental methods on children. +Instead, you can use `S7::super()` to explicitly convert the subclass to a parent before invoking the generic again. + +```{r} +S7::method(build_ggplot, inked_ggplot) <- function(plot, ...) { + # Edit plot to our liking + plot@theme <- theme_gray(ink = plot@ink) + plot@theme + + # Invoke next method + build_ggplot(S7::super(plot, to = class_ggplot), ...) +} + +my_plot +``` + +Just to show that the new property in our subclass works as expected: + +```{r} +my_plot@ink <- "red" +my_plot +``` + +### Your generics with methods for ggplot2's classes + +Alternatively, it might be that you package has generic functions and methods that handle some of ggplot2's classes. +The S7 system has its own way of handling class names, which means that S3 function name patterns of the form `{generic_name}.{class_name}` no longer invoke the correct method for S7 classes. + +```{r, error=TRUE} +# Declare S3 generic +foo <- function(x, ...) { + UseMethod("foo") +} + +# Implement S3 method +foo.labels <- function(x, ...) { + x[] <- lapply(x, toupper) + x +} + +# Burn your fingers +foo(labs(colour = "my lowercase title")) +``` + +Please note that the `ggplot()` and `theme()` still produces objects with the `"ggplot"` and `"theme"` class for backwards compatibility, but this is scheduled to be removed in the future. +The best remedy the dilemma with S3 would be to use `S7::method()`, which also works for S3 generics. + +```{r} +# Note that `foo()` is still an S3 generic +S7::method(foo, class_labels) <- function(x, ...) { + x[] <- lapply(x, toupper) + x +} + +# Note text has updated +foo(labs(colour = "my lowercase title")) +``` + +If that is not an option, because you may not want to depend on S7, you can *currently* use a little hack. +The hack is to prepend the S7 class prefix in the class name of the S3 method. +This prefix is the name of the package that defines the class, followed by `::`. + +```{r} +`foo.ggplot2::labels` <- function(x, ...) { + x[] <- lapply(x, toupper) + x +} +``` + +## Checklist + +Because all of the above might be hard to parse in its entirety, here is a dainty checklist of common migration issues. + + Do I wrap a gglot class that should become an S7 class with extra properties? + + Are there cases where `inherits()` is used which should be replaced with test functions or `S7::S7_inherits()`? + + Do I edit objects with `$`, `[` or `[[` that were previously lists but are now properties to edit with `@`? + + Are there tests that assume S3 classes, that should use `testthat::expect_s7_class()` instead? + + Do I implement methods for one of the S3 generics that should become S7 methods? + + Do I have a generic that may need to facilitate methods for ggplot2's new S7 classes? + + If I assume ggplot2's S7 classes in my code, do I need to bump the required ggplot2 version in the DESCRIPTION file? + +Thank you for reading, we hope that most of it was not necessary! diff --git a/content/blog/ggplot2-4-0-0-s7/index.md b/content/blog/ggplot2-4-0-0-s7/index.md new file mode 100644 index 000000000..fb60b0b16 --- /dev/null +++ b/content/blog/ggplot2-4-0-0-s7/index.md @@ -0,0 +1,371 @@ +--- +output: hugodown::hugo_document + +slug: ggplot2-4-0-0-s7 +title: ggplot2 migrates to S7 +date: 2025-05-26 +author: Teun van den Brand +description: > + The ggplot2 package is migrating to S7 and we'd like to minimise any problems. + This guide details the classes and functions that ggplot2 has migrated and + how these might affect downstream packages. + +photo: + url: https://unsplash.com/photos/n6vS3xlnsCc + author: Kelley Bozarth + +# one of: "deep-dive", "learn", "package", "programming", "roundup", or "other" +categories: [package] +tags: [ggplot2, s7, package maintenance] +rmd_hash: efd5c45c8f4f49e5 + +--- + + + +The ggplot2 package is on the verge to release version 4.0.0. That is right: a new major version release! We only tend to do these when something fundamental changes in ggplot2. For example: ggplot2 2.0.0 brought the ggproto extension system and 3.0.0 switched to tidy evaluation. This time around, we're swapping out the S3 object oriented programming system for the newer S7 system. Because of this major change, we expect that some packages might break, despite our best efforts to minimise the damage. This here is a guide for package authors that might be affected by this change. This guide details some changes in classes and functions that may affect downstream packages, and gives recommendations how broken parts might be repaired. If you don't maintain a package that depends on ggplot2, you can skip reading this guide and simply take away that there will be a release soon. + +## Testing compatibility + +If you are a package author that depends on ggplot2 and you want to know how your package might be affected, you can try the current development version from GitHub using the code below. + +``` r +pak::pak("tidyverse/ggplot2") +``` + +It should also automatically install scales 1.4.0, which is needed for this release. One of the things to inspect first is the result of R CMD check on your package, with the development version of ggplot2 installed. It can be invoked by [`devtools::check()`](https://devtools.r-lib.org/reference/check.html). This is also the check CRAN also runs on your package to keep tabs on if your package continues to work. If you are lucky, this will happily report that there are no problems and you can stop reading this guide! If you are unlucky, it will list errors and warnings associated with running your package. It might be that your examples no longer work, test assumptions are no longer met or vignettes run amock. If you use visual snapshots from the vdiffr package, you may certainly expect (mostly harmless) imperceptible changes. + +As you're still reading, I'm assuming there are problems to solve. The next step is determining who should fix these problems. We have tried to facilitate some backwards compatibility, but we also cannot anticipate every contingency. If something is broken with classes, generics, methods or object oriented programming in general, this guide describes problems and remedies. Because ggplot2 does not go back to S3, we hope that you will facilitate the migration to S7 in your code where appropriate. If there are other issues that pop up that you think might be best repaired in ggplot2, you can post an issue in the [issue tracker](https://github.com/tidyverse/ggplot2/issues). + +That said, let's go through S7 a bit. [S7](https://rconsortium.github.io/S7/) is a newer object oriented programming system that is built on top of the older S3 system. It was build by a collaboration of developers from different niches in the R community, ranging from R Core, to Bioconductor to the tidyverse. It aims to succeed the simpler S3 and more complex S4 systems. Aside from simply modernising ggplot2, the migration to S7 also enables features that are hard to implement in S3, such as double dispatch. For years now, people have been asking for more control over how plots are declared at both sides of the `+` operator, which S7 will facilitate. + +## Classes + +The ggplot2 package uses a mixture of object oriented programming systems. One of these systems is the ggproto system that powers the extension mechanism and remains unchanged. The other system is S3 which has been supplanted by S7 in the recent ggplot2 update. You might notice this from the new S7 class objects that ggplot2 defines, like `class_ggplot` or `class_theme`. + +

+ +
library(ggplot2)
+class_ggplot
+#> <ggplot2::ggplot> class
+#> @ parent     : <ggplot2::gg>
+#> @ constructor: function(data, layers, scales, guides, mapping, theme, coordinates, facet, layout, labels, meta, plot_env) {...}
+#> @ validator  : <NULL>
+#> @ properties :
+#>  $ data       : <ANY>             
+#>  $ layers     : <list>            
+#>  $ scales     : S3<ScalesList>    
+#>  $ guides     : S3<Guides>        
+#>  $ mapping    : <ggplot2::mapping>
+#>  $ theme      : <ggplot2::theme>  
+#>  $ coordinates: S3<Coord>         
+#>  $ facet      : S3<Facet>         
+#>  $ layout     : S3<Layout>        
+#>  $ labels     : <ggplot2::labels> 
+#>  $ meta       : <list>            
+#>  $ plot_env   : <environment>
+
+ +
+ +### Properties + +In prior incarnations, ggplot2 defined the ggplot class as a named list with the `"ggplot"` class attribute. Classes in S7 are more formal than in S3 and have properties which can have restricted classes. For example, in the ggplot class, the `data` property can be anything (because it will go through [`fortify()`](https://ggplot2.tidyverse.org/reference/fortify.html) to become a data frame), the `facet` property must be the `Facet` ggproto class, and the `theme` property must be an S7 theme object. + +In contrast to S3, we cannot simply add new items to `ggplot` object [^1]. The way to add additional information to classes in S7 is to make a subclass with additional properties. For example, if we want to add for example colour information to a new plot, we can do the following: + +
+ +
inked_ggplot <- S7::new_class(
+  name = "inked_ggplot",
+  parent = class_ggplot, 
+  properties = list(ink = S7::class_character)
+)
+inked_ggplot
+#> <inked_ggplot> class
+#> @ parent     : <ggplot2::ggplot>
+#> @ constructor: function(data, layers, scales, guides, mapping, theme, coordinates, facet, layout, labels, meta, plot_env, ink) {...}
+#> @ validator  : <NULL>
+#> @ properties :
+#>  $ data       : <ANY>             
+#>  $ layers     : <list>            
+#>  $ scales     : S3<ScalesList>    
+#>  $ guides     : S3<Guides>        
+#>  $ mapping    : <ggplot2::mapping>
+#>  $ theme      : <ggplot2::theme>  
+#>  $ coordinates: S3<Coord>         
+#>  $ facet      : S3<Facet>         
+#>  $ layout     : S3<Layout>        
+#>  $ labels     : <ggplot2::labels> 
+#>  $ meta       : <list>            
+#>  $ plot_env   : <environment>     
+#>  $ ink        : <character>
+
+ +
+ +When you define a new class, the object you've assigned it to automatically becomes the class definition which comes with a free, standard constructor. This means that we can start building new plots with our subclass right away. Note that we haven't implemented any behaviour around the `ink` property (yet), so it will just print like a normal plot. + +
+ +
my_plot <- inked_ggplot(data = mpg, ink = "red") +
+  geom_point(aes(displ, hwy))
+my_plot
+
+ + +
+ +In contrast to S3, where you would change list-items by using `$`, in S7 you can use `@` to read and write properties. So if we want to change the stored `ink` colour, we can use: + +
+ +
my_plot@ink <- "blue"
+ +
+ +### Testing + +In S3, the recommended way to test for the class of an object is to use a testing function. For example [`is.factor()`](https://rdrr.io/r/base/factor.html); and if such a testing function doesn't exist: use [`inherits()`](https://rdrr.io/r/base/class.html). In S7, it is still recommended to use dedicating testing functions, but if these are absent, you can use [`S7::S7_inherits()`](https://rconsortium.github.io/S7/reference/S7_inherits.html). If we wanted to write a testing function for our new class, we can do that as follows: + +
+ +
is_inked_ggplot <- function(x) S7::S7_inherits(x, inked_ggplot)
+
+# Is not our class
+is_inked_ggplot(ggplot())
+#> [1] FALSE
+
+# Is our class
+is_inked_ggplot(my_plot)
+#> [1] TRUE
+
+ +
+ +### Overview + +To give an overview of ggplot2's S7 classes, we include the table below. The table also lists the recommended way to test for the class. + +
+ +| Old S3 Class | New S7 Class | Testing functions | +|:---------------|:------------------|:-------------------------------------| +| "ggplot2" | class_ggplot | `is_ggplot(x)` | +| "ggplot_built" | class_ggplot_built | `S7::S7_inherits(x, class_ggplot_built)` | +| "labs" | class_labels | `S7::S7_inherits(x, class_labels)` | +| "uneval" | class_mapping | `is_mapping(x)` | +| "theme" | class_theme | `is_theme(x)` | +| "element_blank" | element_blank | `is_theme_element(x, "blank")` | +| "element_line" | element_line | `is_theme_element(x, "line")` | +| "element_rect" | element_rect | `is_theme_element(x, "rect")` | +| "element_text" | element_text | `is_theme_element(x, "text")` | +| NA | element_polygon | `is_theme_element(x, "polygon")` | +| NA | element_point | `is_theme_element(x, "point")` | +| NA | element_geom | `is_theme_element(x, "geom")` | + +
+ +### Testing + +It should be noted that the `is_*()` testing already know about the S7-ness of the new classes. This is handy when it comes to test expectations, because the testing function can be used instead of the S3/S7 class expectations. Previously, you might have used [`testthat::expect_s3_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html), but it is better now to test with [`testthat::expect_s7_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html) or use an `is_*()` function instead. + +
+ +
testthat::test_that(
+  "the plot object has the ggplot class",
+  {
+    plot <- ggplot()
+    
+    # Works regardless of S3 or S7
+    testthat::expect_true(is_ggplot(plot))
+    
+    # This will become dysfunctional in the future.
+    # Do not use this!
+    testthat::expect_s3_class(plot, "ggplot")
+    
+    # This will work in the new version
+    testthat::expect_s7_class(plot, class_ggplot)
+  }
+)
+#> Test passed 😸
+
+ +
+ +The ggplot2 package manually appends the `"ggplot"` class for backwards compatibility reasons (likewise for `"theme"`). However once this phases out, the [`testthat::expect_s3_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html) expectation will become untenable. It is also currently flawed, as it does not work for subclasses! + +
+ +
testthat::test_that(
+  "the inked plot has the ggplot class",
+  {
+    plot <- inked_ggplot()
+    testthat::expect_s3_class(plot, "ggplot")
+  }
+)
+#> ── Failure: the inked plot has the ggplot class ────────────────────────────────
+#> `plot` inherits from 'inked_ggplot'/'ggplot2::ggplot'/'ggplot2::gg'/'S7_object' not 'ggplot'.
+#> Error:
+#> ! Test failed
+
+ +
+ +The advice herein is thus to use [`is_ggplot()`](https://ggplot2.tidyverse.org/reference/is_tests.html). + +## Generics and methods + +If you are new to object oriented programming in R, you might be unfamiliar what the terms 'generic' and 'methods' mean. They are a form of 'polymorphism', where we can use a single function, called the 'generic' function, with different implementations for different classes (where one such implementation is called a 'method'). A well known generic is [`print()`](https://rdrr.io/r/base/print.html), which does different things for different classes. For example `print(1:10)` prints the numeric vector to the console, but `print(my_plot)` opens a graphics device to render the plot. + +### Your methods for ggplot's generics + +The ggplot2 package also declares some generic functions and contains methods for these, most of which revolve around plot construction. The migration to S7 means that the generics and methods defined by ggplot2 also migrate. While it is possible to define S7 methods for S3 generics, it is not possible to define S3 methods for S7 generics. + +
+ +
# Declare an S7 generic
+apply_ink <- S7::new_generic("apply_ink", "plot")
+
+# Attempt to implement an S3 method
+apply_ink.inked_ggplot <- function(plot, ...) {
+  # Edit plot to our liking
+  plot@theme <- theme_gray(ink = plot@ink) + plot@theme
+  plot
+}
+
+# Burn your fingers
+apply_ink(my_plot)
+#> Error: Can't find method for `apply_ink(<inked_ggplot>)`.
+
+ +
+ +To allow for a smoother transition from S3 to S7, we plan to keep S3 generics around for another release cycle but will permanently disable them in the future in favour of the S7 generics. Here is an overview of which S7 generics supplant which S3 generics: + +
+ +| Old S3 Generic | New S7 Generic | Description | +|:--------------|:--------------|:------------------------------------------| +| [`ggplot_add()`](https://ggplot2.tidyverse.org/reference/update_ggplot.html) | [`update_ggplot()`](https://ggplot2.tidyverse.org/reference/update_ggplot.html) | Determines what happens when you `+` an object to a plot. | +| [`ggplot_build()`](https://ggplot2.tidyverse.org/reference/build_ggplot.html) | [`build_ggplot()`](https://ggplot2.tidyverse.org/reference/build_ggplot.html) | Processes data for display in a plot. | +| [`ggplot_gtable()`](https://ggplot2.tidyverse.org/reference/gtable_ggplot.html) | [`gtable_ggplot()`](https://ggplot2.tidyverse.org/reference/gtable_ggplot.html) | Renders a processed plot plot to a gtable object. | +| [`element_grob()`](https://ggplot2.tidyverse.org/reference/draw_element.html) | [`draw_element()`](https://ggplot2.tidyverse.org/reference/draw_element.html) | Renders a theme element. | + +
+ +If your package implements methods for one of the old S3 generics, we recommend to replace these with S7 in a timely manner. An important difference between S3 and S7 is that S7 does not use [`NextMethod()`](https://rdrr.io/r/base/UseMethod.html) to magically invoke parental methods on children. Instead, you can use [`S7::super()`](https://rconsortium.github.io/S7/reference/super.html) to explicitly convert the subclass to a parent before invoking the generic again. + +
+ +
S7::method(build_ggplot, inked_ggplot) <- function(plot, ...) {
+  # Edit plot to our liking
+  plot@theme <- theme_gray(ink = plot@ink) + plot@theme
+  
+  # Invoke next method
+  build_ggplot(S7::super(plot, to = class_ggplot), ...)
+}
+
+my_plot
+
+ + +
+ +Just to show that the new property in our subclass works as expected: + +
+ +
my_plot@ink <- "red"
+my_plot
+
+ + +
+ +### Your generics with methods for ggplot2's classes + +Alternatively, it might be that you package has generic functions and methods that handle some of ggplot2's classes. The S7 system has its own way of handling class names, which means that S3 function name patterns of the form `{generic_name}.{class_name}` no longer invoke the correct method for S7 classes. + +
+ +
# Declare S3 generic
+foo <- function(x, ...) {
+  UseMethod("foo")
+}
+
+# Implement S3 method
+foo.labels <- function(x, ...) {
+  x[] <- lapply(x, toupper)
+  x
+}
+
+# Burn your fingers
+foo(labs(colour = "my lowercase title"))
+#> Error in UseMethod("foo"): no applicable method for 'foo' applied to an object of class "c('ggplot2::labels', 'gg', 'S7_object')"
+
+ +
+ +Please note that the [`ggplot()`](https://ggplot2.tidyverse.org/reference/ggplot.html) and [`theme()`](https://ggplot2.tidyverse.org/reference/theme.html) still produces objects with the `"ggplot"` and `"theme"` class for backwards compatibility, but this is scheduled to be removed in the future. The best remedy the dilemma with S3 would be to use [`S7::method()`](https://rconsortium.github.io/S7/reference/method.html), which also works for S3 generics. + +
+ +
# Note that `foo()` is still an S3 generic
+S7::method(foo, class_labels) <- function(x, ...) {
+  x[] <- lapply(x, toupper)
+  x
+}
+
+# Note text has updated
+foo(labs(colour = "my lowercase title"))
+#> <ggplot2::labels> List of 1
+#>  $ colour: chr "MY LOWERCASE TITLE"
+
+ +
+ +If that is not an option, because you may not want to depend on S7, you can *currently* use a little hack. The hack is to prepend the S7 class prefix in the class name of the S3 method. This prefix is the name of the package that defines the class, followed by `::`. + +
+ +
`foo.ggplot2::labels` <- function(x, ...) {
+  x[] <- lapply(x, toupper)
+  x
+}
+ +
+ +## Checklist + +Because all of the above might be hard to parse in its entirety, here is a dainty checklist of common migration issues. + + Do I wrap a gglot class that should become an S7 class with extra properties? + + Are there cases where [`inherits()`](https://rdrr.io/r/base/class.html) is used which should be replaced with test functions or [`S7::S7_inherits()`](https://rconsortium.github.io/S7/reference/S7_inherits.html)? + + Do I edit objects with `$`, `[` or `[[` that were previously lists but are now properties to edit with `@`? + + Are there tests that assume S3 classes, that should use [`testthat::expect_s7_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html) instead? + + Do I implement methods for one of the S3 generics that should become S7 methods? + + Do I have a generic that may need to facilitate methods for ggplot2's new S7 classes? + + If I assume ggplot2's S7 classes in my code, do I need to bump the required ggplot2 version in the DESCRIPTION file? + +Thank you for reading, we hope that most of it was not necessary! + +[^1]: This still 'works' for backwards compatibility reasons, but it will be phased out in the future, so it should be avoided. + diff --git a/content/blog/ggplot2-4-0-0-s7/thumbnail-sq.jpg b/content/blog/ggplot2-4-0-0-s7/thumbnail-sq.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f8b0561559d6a29ab982e6481d1698a80263b293 GIT binary patch literal 20460 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 literal 0 HcmV?d00001 diff --git a/content/blog/ggplot2-4-0-0-s7/thumbnail-wd.jpg b/content/blog/ggplot2-4-0-0-s7/thumbnail-wd.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5cd5468e20fb3920b0a222028eae84d643d04805 GIT binary patch literal 21611 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 Date: Mon, 26 May 2025 18:28:33 +0200 Subject: [PATCH 2/5] spelling things --- content/blog/ggplot2-4-0-0-s7/index.Rmd | 5 +++-- content/blog/ggplot2-4-0-0-s7/index.md | 27 +++++++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/content/blog/ggplot2-4-0-0-s7/index.Rmd b/content/blog/ggplot2-4-0-0-s7/index.Rmd index 47516cb69..4870bd250 100644 --- a/content/blog/ggplot2-4-0-0-s7/index.Rmd +++ b/content/blog/ggplot2-4-0-0-s7/index.Rmd @@ -92,7 +92,7 @@ For example, in the ggplot class, the `data` property can be anything (because i In contrast to S3, we cannot simply add new items to `ggplot` object ^[This still 'works' for backwards compatibility reasons, but it will be phased out in the future, so it should be avoided.]. The way to add additional information to classes in S7 is to make a subclass with additional properties. -For example, if we want to add for example colour information to a new plot, we can do the following: +For example, if we want to add colour information to a new plot, we can do the following: ```{r} inked_ggplot <- S7::new_class( @@ -100,6 +100,7 @@ inked_ggplot <- S7::new_class( parent = class_ggplot, properties = list(ink = S7::class_character) ) + inked_ggplot ``` @@ -239,7 +240,7 @@ generics <- tibble::tribble( ~`Old S3 Generic`, ~`New S7 Generic`, ~`Description`, "`ggplot_add()`", "`update_ggplot()`", "Determines what happens when you `+` an object to a plot.", "`ggplot_build()`", "`build_ggplot()`", "Processes data for display in a plot.", - "`ggplot_gtable()`", "`gtable_ggplot()`", "Renders a processed plot plot to a gtable object.", + "`ggplot_gtable()`", "`gtable_ggplot()`", "Renders a processed plot to a gtable object.", "`element_grob()`", "`draw_element()`", "Renders a theme element." ) knitr::kable(generics) diff --git a/content/blog/ggplot2-4-0-0-s7/index.md b/content/blog/ggplot2-4-0-0-s7/index.md index fb60b0b16..e7e0499bc 100644 --- a/content/blog/ggplot2-4-0-0-s7/index.md +++ b/content/blog/ggplot2-4-0-0-s7/index.md @@ -11,13 +11,13 @@ description: > how these might affect downstream packages. photo: - url: https://unsplash.com/photos/n6vS3xlnsCc - author: Kelley Bozarth + url: https://unsplash.com/photos/silhouette-of-person-standing-on-grass-field-during-sunset-Fr33DHTpLZk + author: Inhyeok Park # one of: "deep-dive", "learn", "package", "programming", "roundup", or "other" categories: [package] tags: [ggplot2, s7, package maintenance] -rmd_hash: efd5c45c8f4f49e5 +rmd_hash: 8e08128ee559474a --- @@ -26,10 +26,10 @@ TODO: * [x] Look over / edit the post's title in the yaml * [x] Edit (or delete) the description; note this appears in the Twitter card * [x] Pick category and tags (see existing with [`hugodown::tidy_show_meta()`](https://rdrr.io/pkg/hugodown/man/use_tidy_post.html)) -* [ ] Find photo & update yaml metadata -* [ ] Create `thumbnail-sq.jpg`; height and width should be equal -* [ ] Create `thumbnail-wd.jpg`; width should be >5x height -* [ ] [`hugodown::use_tidy_thumbnails()`](https://rdrr.io/pkg/hugodown/man/use_tidy_post.html) +* [x] Find photo & update yaml metadata +* [x] Create `thumbnail-sq.jpg`; height and width should be equal +* [x] Create `thumbnail-wd.jpg`; width should be >5x height +* [x] [`hugodown::use_tidy_thumbnails()`](https://rdrr.io/pkg/hugodown/man/use_tidy_post.html) * [ ] Add intro sentence, e.g. the standard tagline for the package * [ ] [`usethis::use_tidy_thanks()`](https://usethis.r-lib.org/reference/use_tidy_thanks.html) --> @@ -83,7 +83,7 @@ The ggplot2 package uses a mixture of object oriented programming systems. One o In prior incarnations, ggplot2 defined the ggplot class as a named list with the `"ggplot"` class attribute. Classes in S7 are more formal than in S3 and have properties which can have restricted classes. For example, in the ggplot class, the `data` property can be anything (because it will go through [`fortify()`](https://ggplot2.tidyverse.org/reference/fortify.html) to become a data frame), the `facet` property must be the `Facet` ggproto class, and the `theme` property must be an S7 theme object. -In contrast to S3, we cannot simply add new items to `ggplot` object [^1]. The way to add additional information to classes in S7 is to make a subclass with additional properties. For example, if we want to add for example colour information to a new plot, we can do the following: +In contrast to S3, we cannot simply add new items to `ggplot` object [^1]. The way to add additional information to classes in S7 is to make a subclass with additional properties. For example, if we want to add colour information to a new plot, we can do the following:

@@ -92,6 +92,7 @@ In contrast to S3, we cannot simply add new items to `ggplot` object [^1]. The w parent = class_ggplot, properties = list(ink = S7::class_character) ) + inked_ggplot #> <inked_ggplot> class #> @ parent : <ggplot2::ggplot> @@ -137,7 +138,7 @@ In contrast to S3, where you would change list-items by using `$`, in S7 you can ### Testing -In S3, the recommended way to test for the class of an object is to use a testing function. For example [`is.factor()`](https://rdrr.io/r/base/factor.html); and if such a testing function doesn't exist: use [`inherits()`](https://rdrr.io/r/base/class.html). In S7, it is still recommended to use dedicating testing functions, but if these are absent, you can use [`S7::S7_inherits()`](https://rconsortium.github.io/S7/reference/S7_inherits.html). If we wanted to write a testing function for our new class, we can do that as follows: +In S3, the recommended way to test for the class of an object is to use a testing function. For example [`is.factor()`](https://rdrr.io/r/base/factor.html); and if such a testing function doesn't exist: use [`inherits()`](https://rdrr.io/r/base/class.html). In S7, it is still recommended to use dedicating testing functions. However, if these are absent, you can use [`S7::S7_inherits()`](https://rconsortium.github.io/S7/reference/S7_inherits.html). If we wanted to write a testing function for our new class, we can do that as follows:
@@ -179,7 +180,7 @@ To give an overview of ggplot2's S7 classes, we include the table below. The tab ### Testing -It should be noted that the `is_*()` testing already know about the S7-ness of the new classes. This is handy when it comes to test expectations, because the testing function can be used instead of the S3/S7 class expectations. Previously, you might have used [`testthat::expect_s3_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html), but it is better now to test with [`testthat::expect_s7_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html) or use an `is_*()` function instead. +It should be noted that the `is_*()` testing functions in ggplot2 already know about the S7-ness of the new classes. This is handy when it comes to test expectations, because the testing function can be used instead of the S3/S7 class expectations. Previously, you might have used [`testthat::expect_s3_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html), but it is better now to test with [`testthat::expect_s7_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html) or use an `is_*()` function instead.
@@ -199,12 +200,12 @@ It should be noted that the `is_*()` testing already know about the S7-ness of t testthat::expect_s7_class(plot, class_ggplot) } ) -#> Test passed 😸 +#> Test passed 🥇
-The ggplot2 package manually appends the `"ggplot"` class for backwards compatibility reasons (likewise for `"theme"`). However once this phases out, the [`testthat::expect_s3_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html) expectation will become untenable. It is also currently flawed, as it does not work for subclasses! +The ggplot2 package manually appends the `"ggplot"` class for backwards compatibility reasons (likewise for `"theme"`). However, once this phases out, the [`testthat::expect_s3_class()`](https://testthat.r-lib.org/reference/inheritance-expectations.html) expectation will become untenable. It is also currently flawed, as it does not work for subclasses!
@@ -260,7 +261,7 @@ To allow for a smoother transition from S3 to S7, we plan to keep S3 generics ar |:--------------|:--------------|:------------------------------------------| | [`ggplot_add()`](https://ggplot2.tidyverse.org/reference/update_ggplot.html) | [`update_ggplot()`](https://ggplot2.tidyverse.org/reference/update_ggplot.html) | Determines what happens when you `+` an object to a plot. | | [`ggplot_build()`](https://ggplot2.tidyverse.org/reference/build_ggplot.html) | [`build_ggplot()`](https://ggplot2.tidyverse.org/reference/build_ggplot.html) | Processes data for display in a plot. | -| [`ggplot_gtable()`](https://ggplot2.tidyverse.org/reference/gtable_ggplot.html) | [`gtable_ggplot()`](https://ggplot2.tidyverse.org/reference/gtable_ggplot.html) | Renders a processed plot plot to a gtable object. | +| [`ggplot_gtable()`](https://ggplot2.tidyverse.org/reference/gtable_ggplot.html) | [`gtable_ggplot()`](https://ggplot2.tidyverse.org/reference/gtable_ggplot.html) | Renders a processed plot to a gtable object. | | [`element_grob()`](https://ggplot2.tidyverse.org/reference/draw_element.html) | [`draw_element()`](https://ggplot2.tidyverse.org/reference/draw_element.html) | Renders a theme element. |
From f9262b48c7143b1dfd4ed45d30cf02fe76a8aa73 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 26 May 2025 18:31:50 +0200 Subject: [PATCH 3/5] mention `S7::methods_register()` --- content/blog/ggplot2-4-0-0-s7/index.Rmd | 3 +++ content/blog/ggplot2-4-0-0-s7/index.md | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/content/blog/ggplot2-4-0-0-s7/index.Rmd b/content/blog/ggplot2-4-0-0-s7/index.Rmd index 4870bd250..21a993db8 100644 --- a/content/blog/ggplot2-4-0-0-s7/index.Rmd +++ b/content/blog/ggplot2-4-0-0-s7/index.Rmd @@ -215,6 +215,9 @@ For example `print(1:10)` prints the numeric vector to the console, but `print(m The ggplot2 package also declares some generic functions and contains methods for these, most of which revolve around plot construction. The migration to S7 means that the generics and methods defined by ggplot2 also migrate. + +It is also good to mention that when your package registers a method for one of ggplot2's generics, ggplot2's generic is called an 'external generic' from the point of view of your package. With S7, you should include `S7::methods_register()` in your package's `.onLoad()` call. + While it is possible to define S7 methods for S3 generics, it is not possible to define S3 methods for S7 generics. ```{r, error=TRUE} diff --git a/content/blog/ggplot2-4-0-0-s7/index.md b/content/blog/ggplot2-4-0-0-s7/index.md index e7e0499bc..078c5f67c 100644 --- a/content/blog/ggplot2-4-0-0-s7/index.md +++ b/content/blog/ggplot2-4-0-0-s7/index.md @@ -17,7 +17,7 @@ photo: # one of: "deep-dive", "learn", "package", "programming", "roundup", or "other" categories: [package] tags: [ggplot2, s7, package maintenance] -rmd_hash: 8e08128ee559474a +rmd_hash: 0c606e7113efea7f --- @@ -200,7 +200,7 @@ It should be noted that the `is_*()` testing functions in ggplot2 already know a testthat::expect_s7_class(plot, class_ggplot) } ) -#> Test passed 🥇 +#> Test passed 🎉
@@ -232,7 +232,11 @@ If you are new to object oriented programming in R, you might be unfamiliar what ### Your methods for ggplot's generics -The ggplot2 package also declares some generic functions and contains methods for these, most of which revolve around plot construction. The migration to S7 means that the generics and methods defined by ggplot2 also migrate. While it is possible to define S7 methods for S3 generics, it is not possible to define S3 methods for S7 generics. +The ggplot2 package also declares some generic functions and contains methods for these, most of which revolve around plot construction. The migration to S7 means that the generics and methods defined by ggplot2 also migrate. + +It is also good to mention that when your package registers a method for one of ggplot2's generics, ggplot2's generic is called an 'external generic' from the point of view of your package. With S7, you should include [`S7::methods_register()`](https://rconsortium.github.io/S7/reference/methods_register.html) in your package's `.onLoad()` call. + +While it is possible to define S7 methods for S3 generics, it is not possible to define S3 methods for S7 generics.
From b1ce125b1f7f4f11d81653019a8f2c27129b255f Mon Sep 17 00:00:00 2001 From: Teun van den Brand <49372158+teunbrand@users.noreply.github.com> Date: Tue, 27 May 2025 10:50:37 +0200 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Thomas Lin Pedersen --- content/blog/ggplot2-4-0-0-s7/index.Rmd | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/content/blog/ggplot2-4-0-0-s7/index.Rmd b/content/blog/ggplot2-4-0-0-s7/index.Rmd index 21a993db8..0fe0afee3 100644 --- a/content/blog/ggplot2-4-0-0-s7/index.Rmd +++ b/content/blog/ggplot2-4-0-0-s7/index.Rmd @@ -32,14 +32,14 @@ TODO: * [ ] `usethis::use_tidy_thanks()` --> -The ggplot2 package is on the verge to release version 4.0.0. +We are on the verge of releasing version 4.0.0 of the ggplot2 package. That is right: a new major version release! We only tend to do these when something fundamental changes in ggplot2. For example: ggplot2 2.0.0 brought the ggproto extension system and 3.0.0 switched to tidy evaluation. This time around, we're swapping out the S3 object oriented programming system for the newer S7 system. -Because of this major change, we expect that some packages might break, despite our best efforts to minimise the damage. -This here is a guide for package authors that might be affected by this change. -This guide details some changes in classes and functions that may affect downstream packages, and gives recommendations how broken parts might be repaired. +Because of this major change, we expect that some packages might break, despite our best efforts to minimise the implications of the switch. +This here is a guide for package authors that might be affected by the changes. +It details some changes in classes and functions that may affect downstream packages, and gives recommendations how broken parts might be repaired. If you don't maintain a package that depends on ggplot2, you can skip reading this guide and simply take away that there will be a release soon. ## Testing compatibility @@ -53,8 +53,8 @@ pak::pak("tidyverse/ggplot2") It should also automatically install scales 1.4.0, which is needed for this release. One of the things to inspect first is the result of R CMD check on your package, with the development version of ggplot2 installed. It can be invoked by `devtools::check()`. -This is also the check CRAN also runs on your package to keep tabs on if your package continues to work. -If you are lucky, this will happily report that there are no problems and you can stop reading this guide! +This is also the check CRAN runs on your package to keep tabs on whether your package continues to work. +If you are lucky, it will happily report that there are no problems and you can stop reading this guide! If you are unlucky, it will list errors and warnings associated with running your package. It might be that your examples no longer work, test assumptions are no longer met or vignettes run amock. If you use visual snapshots from the vdiffr package, you may certainly expect (mostly harmless) imperceptible changes. @@ -67,7 +67,7 @@ Because ggplot2 does not go back to S3, we hope that you will facilitate the mig If there are other issues that pop up that you think might be best repaired in ggplot2, you can post an issue in the [issue tracker](https://github.com/tidyverse/ggplot2/issues). That said, let's go through S7 a bit. [S7](https://rconsortium.github.io/S7/) is a newer object oriented programming system that is built on top of the older S3 system. -It was build by a collaboration of developers from different niches in the R community, ranging from R Core, to Bioconductor to the tidyverse. +It was build by a collaboration of developers from different parts in the R community, ranging from R Core, to Bioconductor to the tidyverse. It aims to succeed the simpler S3 and more complex S4 systems. Aside from simply modernising ggplot2, the migration to S7 also enables features that are hard to implement in S3, such as double dispatch. For years now, people have been asking for more control over how plots are declared at both sides of the `+` operator, which S7 will facilitate. @@ -124,7 +124,7 @@ my_plot@ink <- "blue" ### Testing In S3, the recommended way to test for the class of an object is to use a testing function. -For example `is.factor()`; and if such a testing function doesn't exist: use `inherits()`. +An example is `is.factor()` but it may be that such a testing function doesn't exist:. In that case you can use `inherits()`. In S7, it is still recommended to use dedicating testing functions. However, if these are absent, you can use `S7::S7_inherits()`. If we wanted to write a testing function for our new class, we can do that as follows: @@ -206,10 +206,10 @@ The advice herein is thus to use `is_ggplot()`. ## Generics and methods -If you are new to object oriented programming in R, you might be unfamiliar what the terms 'generic' and 'methods' mean. -They are a form of 'polymorphism', where we can use a single function, called the 'generic' function, with different implementations for different classes (where one such implementation is called a 'method'). +If you are new to object oriented programming in R, you might be unfamiliar with what the terms 'generic' and 'methods' mean. +They are a form of 'polymorphism', that allow us to use a single function, called the 'generic' function, with different implementations for different classes (where one such implementation is called a 'method'). A well known generic is `print()`, which does different things for different classes. -For example `print(1:10)` prints the numeric vector to the console, but `print(my_plot)` opens a graphics device to render the plot. +For example `print(1:10)` prints the numeric vector to the console, but `print(my_plot)` opens a graphics device and renders the plot. ### Your methods for ggplot's generics From 423e9c9120a21d8146ca558daf555dd24517491c Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 27 May 2025 10:52:14 +0200 Subject: [PATCH 5/5] reknit --- content/blog/ggplot2-4-0-0-s7/index.Rmd | 2 +- content/blog/ggplot2-4-0-0-s7/index.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/content/blog/ggplot2-4-0-0-s7/index.Rmd b/content/blog/ggplot2-4-0-0-s7/index.Rmd index 0fe0afee3..55ed9bbe1 100644 --- a/content/blog/ggplot2-4-0-0-s7/index.Rmd +++ b/content/blog/ggplot2-4-0-0-s7/index.Rmd @@ -124,7 +124,7 @@ my_plot@ink <- "blue" ### Testing In S3, the recommended way to test for the class of an object is to use a testing function. -An example is `is.factor()` but it may be that such a testing function doesn't exist:. In that case you can use `inherits()`. +An example is `is.factor()` but it may be that such a testing function doesn't exist. In that case you can use `inherits()`. In S7, it is still recommended to use dedicating testing functions. However, if these are absent, you can use `S7::S7_inherits()`. If we wanted to write a testing function for our new class, we can do that as follows: diff --git a/content/blog/ggplot2-4-0-0-s7/index.md b/content/blog/ggplot2-4-0-0-s7/index.md index 078c5f67c..005406269 100644 --- a/content/blog/ggplot2-4-0-0-s7/index.md +++ b/content/blog/ggplot2-4-0-0-s7/index.md @@ -17,7 +17,7 @@ photo: # one of: "deep-dive", "learn", "package", "programming", "roundup", or "other" categories: [package] tags: [ggplot2, s7, package maintenance] -rmd_hash: 0c606e7113efea7f +rmd_hash: 9d742fcb8a1f110f --- @@ -34,7 +34,7 @@ TODO: * [ ] [`usethis::use_tidy_thanks()`](https://usethis.r-lib.org/reference/use_tidy_thanks.html) --> -The ggplot2 package is on the verge to release version 4.0.0. That is right: a new major version release! We only tend to do these when something fundamental changes in ggplot2. For example: ggplot2 2.0.0 brought the ggproto extension system and 3.0.0 switched to tidy evaluation. This time around, we're swapping out the S3 object oriented programming system for the newer S7 system. Because of this major change, we expect that some packages might break, despite our best efforts to minimise the damage. This here is a guide for package authors that might be affected by this change. This guide details some changes in classes and functions that may affect downstream packages, and gives recommendations how broken parts might be repaired. If you don't maintain a package that depends on ggplot2, you can skip reading this guide and simply take away that there will be a release soon. +We are on the verge of releasing version 4.0.0 of the ggplot2 package. That is right: a new major version release! We only tend to do these when something fundamental changes in ggplot2. For example: ggplot2 2.0.0 brought the ggproto extension system and 3.0.0 switched to tidy evaluation. This time around, we're swapping out the S3 object oriented programming system for the newer S7 system. Because of this major change, we expect that some packages might break, despite our best efforts to minimise the implications of the switch. This here is a guide for package authors that might be affected by the changes. It details some changes in classes and functions that may affect downstream packages, and gives recommendations how broken parts might be repaired. If you don't maintain a package that depends on ggplot2, you can skip reading this guide and simply take away that there will be a release soon. ## Testing compatibility @@ -44,11 +44,11 @@ If you are a package author that depends on ggplot2 and you want to know how you pak::pak("tidyverse/ggplot2") ``` -It should also automatically install scales 1.4.0, which is needed for this release. One of the things to inspect first is the result of R CMD check on your package, with the development version of ggplot2 installed. It can be invoked by [`devtools::check()`](https://devtools.r-lib.org/reference/check.html). This is also the check CRAN also runs on your package to keep tabs on if your package continues to work. If you are lucky, this will happily report that there are no problems and you can stop reading this guide! If you are unlucky, it will list errors and warnings associated with running your package. It might be that your examples no longer work, test assumptions are no longer met or vignettes run amock. If you use visual snapshots from the vdiffr package, you may certainly expect (mostly harmless) imperceptible changes. +It should also automatically install scales 1.4.0, which is needed for this release. One of the things to inspect first is the result of R CMD check on your package, with the development version of ggplot2 installed. It can be invoked by [`devtools::check()`](https://devtools.r-lib.org/reference/check.html). This is also the check CRAN runs on your package to keep tabs on whether your package continues to work. If you are lucky, it will happily report that there are no problems and you can stop reading this guide! If you are unlucky, it will list errors and warnings associated with running your package. It might be that your examples no longer work, test assumptions are no longer met or vignettes run amock. If you use visual snapshots from the vdiffr package, you may certainly expect (mostly harmless) imperceptible changes. As you're still reading, I'm assuming there are problems to solve. The next step is determining who should fix these problems. We have tried to facilitate some backwards compatibility, but we also cannot anticipate every contingency. If something is broken with classes, generics, methods or object oriented programming in general, this guide describes problems and remedies. Because ggplot2 does not go back to S3, we hope that you will facilitate the migration to S7 in your code where appropriate. If there are other issues that pop up that you think might be best repaired in ggplot2, you can post an issue in the [issue tracker](https://github.com/tidyverse/ggplot2/issues). -That said, let's go through S7 a bit. [S7](https://rconsortium.github.io/S7/) is a newer object oriented programming system that is built on top of the older S3 system. It was build by a collaboration of developers from different niches in the R community, ranging from R Core, to Bioconductor to the tidyverse. It aims to succeed the simpler S3 and more complex S4 systems. Aside from simply modernising ggplot2, the migration to S7 also enables features that are hard to implement in S3, such as double dispatch. For years now, people have been asking for more control over how plots are declared at both sides of the `+` operator, which S7 will facilitate. +That said, let's go through S7 a bit. [S7](https://rconsortium.github.io/S7/) is a newer object oriented programming system that is built on top of the older S3 system. It was build by a collaboration of developers from different parts in the R community, ranging from R Core, to Bioconductor to the tidyverse. It aims to succeed the simpler S3 and more complex S4 systems. Aside from simply modernising ggplot2, the migration to S7 also enables features that are hard to implement in S3, such as double dispatch. For years now, people have been asking for more control over how plots are declared at both sides of the `+` operator, which S7 will facilitate. ## Classes @@ -138,7 +138,7 @@ In contrast to S3, where you would change list-items by using `$`, in S7 you can ### Testing -In S3, the recommended way to test for the class of an object is to use a testing function. For example [`is.factor()`](https://rdrr.io/r/base/factor.html); and if such a testing function doesn't exist: use [`inherits()`](https://rdrr.io/r/base/class.html). In S7, it is still recommended to use dedicating testing functions. However, if these are absent, you can use [`S7::S7_inherits()`](https://rconsortium.github.io/S7/reference/S7_inherits.html). If we wanted to write a testing function for our new class, we can do that as follows: +In S3, the recommended way to test for the class of an object is to use a testing function. An example is [`is.factor()`](https://rdrr.io/r/base/factor.html) but it may be that such a testing function doesn't exist. In that case you can use [`inherits()`](https://rdrr.io/r/base/class.html). In S7, it is still recommended to use dedicating testing functions. However, if these are absent, you can use [`S7::S7_inherits()`](https://rconsortium.github.io/S7/reference/S7_inherits.html). If we wanted to write a testing function for our new class, we can do that as follows:
@@ -228,7 +228,7 @@ The advice herein is thus to use [`is_ggplot()`](https://ggplot2.tidyverse.org/r ## Generics and methods -If you are new to object oriented programming in R, you might be unfamiliar what the terms 'generic' and 'methods' mean. They are a form of 'polymorphism', where we can use a single function, called the 'generic' function, with different implementations for different classes (where one such implementation is called a 'method'). A well known generic is [`print()`](https://rdrr.io/r/base/print.html), which does different things for different classes. For example `print(1:10)` prints the numeric vector to the console, but `print(my_plot)` opens a graphics device to render the plot. +If you are new to object oriented programming in R, you might be unfamiliar with what the terms 'generic' and 'methods' mean. They are a form of 'polymorphism', that allow us to use a single function, called the 'generic' function, with different implementations for different classes (where one such implementation is called a 'method'). A well known generic is [`print()`](https://rdrr.io/r/base/print.html), which does different things for different classes. For example `print(1:10)` prints the numeric vector to the console, but `print(my_plot)` opens a graphics device and renders the plot. ### Your methods for ggplot's generics