depends <- attr(pkgsearch::advanced_search(Depends = "R6"), "metadata")$totalimports <- attr(pkgsearch::advanced_search(Imports = "R6"), "metadata")$totalsuggests <- attr(pkgsearch::advanced_search(Suggests = "R6"), "metadata")$totalglue("{depends + imports + suggests} packages on CRAN utilize `R6`.")
## 321 packages on CRAN utilize `R6`.
pkgsearch::advanced_search(Imports = "R6", size = 300) %>% arrange(-downloads_last_month) %>% head(20) %>% pull("package") %>% paste0(collapse = ", ") %>% strwrap(width = 100) %>% cli::cat_line()
## dplyr, processx, pkgbuild, testthat, callr, vdiffr, desc, httr, scales, promises, selectr, httpuv,## readr, shiny, progress, dbplyr, roxygen2, crosstalk, rcmdcheck, data.tree
"testthat" %>% getNamespace() %>% eapply(is.R6Class) %>% flatten_lgl() %>% which() %>% names()
## [1] "Stack" "TeamcityReporter" "RstudioReporter" "StopReporter" ## [5] "MultiReporter" "SilentReporter" "Reporter" "TapReporter" ## [9] "MinimalReporter" "CheckReporter" "FailReporter" "LocationReporter"## [13] "ProgressReporter" "JunitReporter" "ListReporter" "SummaryReporter" ## [17] "DebugReporter"
"testthat" %>% getNamespace() %>% eapply(is.R6Class) %>% flatten_lgl() %>% which() %>% names()
## [1] "Stack" "TeamcityReporter" "RstudioReporter" "StopReporter" ## [5] "MultiReporter" "SilentReporter" "Reporter" "TapReporter" ## [9] "MinimalReporter" "CheckReporter" "FailReporter" "LocationReporter"## [13] "ProgressReporter" "JunitReporter" "ListReporter" "SummaryReporter" ## [17] "DebugReporter"
testthat:::Stack
## <Stack> object generator## Public:## initialize: function (init = 20L) ## push: function (..., .list = NULL) ## size: function () ## as_list: function () ## clone: function (deep = FALSE) ## Private:## stack: NULL## count: 0## init: 20## Parent env: <environment: namespace:testthat>## Locked objects: TRUE## Locked class: FALSE## Portable: TRUE
In S3
methods belong to the generic
.
In R6
methods belong to the object itself.
x <- 1y <- xx <- 2y
## [1] 1
x <- new.env()y <- xx$a <- 2y$a
## [1] 2
The simplest R6
class:
R6::R6Class()
## <unnamed> object generator## Public:## clone: function (deep = FALSE) ## Parent env: <environment: R_GlobalEnv>## Locked objects: TRUE## Locked class: FALSE## Portable: TRUE
The simplest R6
class:
R6::R6Class()
## <unnamed> object generator## Public:## clone: function (deep = FALSE) ## Parent env: <environment: R_GlobalEnv>## Locked objects: TRUE## Locked class: FALSE## Portable: TRUE
You should at least provide the first argument, classname
:
Beer <- R6::R6Class("Beer")
The simplest R6
class:
R6::R6Class()
## <unnamed> object generator## Public:## clone: function (deep = FALSE) ## Parent env: <environment: R_GlobalEnv>## Locked objects: TRUE## Locked class: FALSE## Portable: TRUE
You should at least provide the first argument, classname
:
Beer <- R6::R6Class("Beer")
class(Beer)
## [1] "R6ClassGenerator"
I (kinda) Lied
The simplest R6
class:
R6::R6Class()
## <unnamed> object generator## Public:## clone: function (deep = FALSE) ## Parent env: <environment: R_GlobalEnv>## Locked objects: TRUE## Locked class: FALSE## Portable: TRUE
You should at least provide the first argument, classname
:
Beer <- R6::R6Class("Beer")
class(Beer)
## [1] "R6ClassGenerator"
I (kinda) Lied
typeof(Beer)
## [1] "environment"
Wait...what?
Beer <- R6::R6Class("Beer")
Instantiate your class using the generator's new
method
beer <- Beer$new()beer
## <Beer>## Public:## clone: function (deep = FALSE)
We'll talk about cloning later, but you can disable it by passing cloneable = FALSE
when defining the class
Beer <- R6::R6Class("Beer", cloneable = FALSE)Beer$new()
## <Beer>## Public:## :
public
The public
argument to R6::R6Class
accepts a named list of methods and/or objects.
Beer <- R6::R6Class("Beer", public = list(abv = .05))
beer <- Beer$new()beer$abv
## [1] 0.05
Beer <- R6::R6Class( "Beer", public = list( abv = 0.05, percent_abv = function() sprintf("%.1f%%", 100 * self$abv) ))beer <- Beer$new()beer$percent_abv()
## [1] "5.0%"
I've already called created my class generator. Can I modify it?
I've already called created my class generator. Can I modify it?
Beer$rating <- 5
Well that was easy!
I've already called created my class generator. Can I modify it?
Beer$rating <- 5
Well that was easy!
Beer$new()$rating
## NULL
D'oh! I should use the generator's
set
method instead.
I've already called created my class generator. Can I modify it?
Beer$rating <- 5
Well that was easy!
Beer$new()$rating
## NULL
D'oh! I should use the generator's
set
method instead.
Beer$set("public", "rating", 5)Beer$new()$rating
## [1] 5
Woohoo!
Methods and variables defined in one class (the parent) can be accessed in a subclass (the child).
Pass an R6ClassGenerator
name for the inherit
parameter of your class call.
IPA <- R6::R6Class("IPA", inherit = Beer)IPA$new()
## <IPA>## Inherits from: <Beer>## Public:## abv: 0.05## clone: function (deep = FALSE) ## percent_abv: function () ## rating: 5
Warning The name is evaluated in the parent_env
during instantiation. We will see this later,
Instantiated R6
classes are assigned an S3
class that reflects its R6
hierarchy.
DoubleIPA <- R6::R6Class("DoubleIPA", inherit = IPA)dipa <- DoubleIPA$new()class(dipa)
## [1] "DoubleIPA" "IPA" "Beer" "R6"
Instantiated R6
classes are assigned an S3
class that reflects its R6
hierarchy.
DoubleIPA <- R6::R6Class("DoubleIPA", inherit = IPA)dipa <- DoubleIPA$new()class(dipa)
## [1] "DoubleIPA" "IPA" "Beer" "R6"
Unless you don't want it to do that.
NonAlcoholic <- R6::R6Class( "NonAlcoholic", inherit = Beer, class = FALSE)zima <- NonAlcoholic$new()class(zima)
## [1] "environment"
private
Variables and methods that are part of the internal behavior of the class
Useful to separate the public
interface, similar to non-exported objects in a package.
Beer$set("private", ".name", "Duff")beer_new <- Beer$new()beer_new$.name
## NULL
private
Variables and methods that are part of the internal behavior of the class
Useful to separate the public
interface, similar to non-exported objects in a package.
Beer$set("private", ".name", "Duff")beer_new <- Beer$new()beer_new$.name
## NULL
Access using private$
Beer$set("public", "get_name", function() { private$.name })beer_new <- Beer$new()beer_new$get_name()
## [1] "Duff"
If you just have to peek
beer_new$.__enclos_env__$private$.name
## [1] "Duff"
active
R6
makes use of 's makeActiveBinding
function to add active/lazy/delayed bindings.
active
R6
makes use of 's makeActiveBinding
function to add active/lazy/delayed bindings.
No variable passed Treated like a variable
One variable passed Treated like an assignment
active
R6
makes use of 's makeActiveBinding
function to add active/lazy/delayed bindings.
No variable passed Treated like a variable
One variable passed Treated like an assignment
Two variables passed Hey now, let's not get crazy.
active
R6
makes use of 's makeActiveBinding
function to add active/lazy/delayed bindings.
No variable passed Treated like a variable
One variable passed Treated like an assignment
Two variables passed Hey now, let's not get crazy.
Beer$set("active", "name", function() { private$.name })new_beer <- Beer$new()new_beer$name
## [1] "Duff"
new_beer$name <- "Duff Lite"
## Error in (function () : unused argument (base::quote("Duff Lite"))
Beer$set("active", "name", function(n) { if (missing(n)) { return(private$.name) } stopifnot(is.character(n) && length(n) == 1) private$.name <- n }, overwrite = TRUE)new_beer <- Beer$new()new_beer$name <- "Duff Life"new_beer$name
## [1] "Duff Life"
initialize
The initialize
method is called at the end of the generator's new
function.
Beer$set("public", "initialize", function(name, rating = 5) { self$name <- name self$rating <- rating cli::cat_line(glue("It's five o'clock somewhere. Give me a {self$name}!")) })beer <- Beer$new("Breakfast Stout")
## It's five o'clock somewhere. Give me a Breakfast Stout!
initialize
The initialize
method is called at the end of the generator's new
function.
Beer$set("public", "initialize", function(name, rating = 5) { self$name <- name self$rating <- rating cli::cat_line(glue("It's five o'clock somewhere. Give me a {self$name}!")) })beer <- Beer$new("Breakfast Stout")
## It's five o'clock somewhere. Give me a Breakfast Stout!
You can call the inherited initialize
function using super$
.
IPA$set("public", "initialize", function(name, ...) { name <- snakecase::to_any_case(name, 'random') super$initialize(name, ...) })beer <- IPA$new("King Sue")
## It's five o'clock somewhere. Give me a KInG sUe!
initialize
If any public or private fields have reference semantics (other R6
classes/environment
s/data.table
s) then they should be created in the initialize
method to avoid sharing.
Sometimes, this can be useful.
`%||%` <- rlang::`%||%`SelfCounter <- R6::R6Class( "SelfCounter", public = list( count_env = new.env(), initialize = function() self$count_env$counter <- (self$count_env$counter %||% 0) + 1 ))
SelfCounter$new()$count_env$counter
## [1] 1
SelfCounter$new()$count_env$counter
## [1] 2
SelfCounter$new()$count_env$counter
## [1] 3
initialize
LessUseful <- R6::R6Class( "LessUseful", public = list( count_env = NULL, initialize = function() { self$count_env <- new.env() self$count_env$counter <- (self$count_env$counter %||% 0) + 1 }))
LessUseful$new()$count_env$counter
## [1] 1
LessUseful$new()$count_env$counter
## [1] 1
LessUseful$new()$count_env$counter
## [1] 1
finalize
The finalize
method allows a class to clean up after itself.
e.g.,
Close database connections
Close file connections
Provide status updates
Beer$set("public", "finalize", function() { cat("Goodnight brew")})beer <- IPA$new("Sculpin")
## It's five o'clock somewhere. Give me a sCulPIN!
# we hardly knew yerm(beer)# force a garbage collection to get the finalizer to triggerinvisible(gc())
## Goodnight brew
print
R6:::print.R6
## function (x, ...) ## {## if (is.function(.subset2(x, "print"))) {## .subset2(x, "print")(...)## }## else {## cat(format(x, ...), sep = "\n")## }## invisible(x)## }## <bytecode: 0x000000001d6655f8>## <environment: namespace:R6>
Define your own print
in the public
members of your class.
Beer$set( "public", "print", function(...) { cat( glue("{self$name}:{strrep('*',self$rating)}") ) })
beer <- Beer$new("Duff", 3)
## It's five o'clock somewhere. Give me a Duff!
beer
## Duff:***
A format
generic is also defined for R6
objects.
beer1 <- Beer$new("Duff")
## It's five o'clock somewhere. Give me a Duff!
beer2 <- beer1beer2$name <- "Duff Lite"beer2
## Duff Lite:*****
beer1
## Duff Lite:*****
beer1 <- Beer$new("Duff")
## It's five o'clock somewhere. Give me a Duff!
beer2 <- beer1$clone()beer2$name <- "Duff Lite"beer2
## Duff Lite:*****
beer1
## Duff:*****
Additional option deep = TRUE
used when you want to make copies of all encapsulated objects that use reference semantics.
Beer$set("private", ".bottles", 9L)Beer$set("active", "bottles", function(b) { if (missing(b)) { return(private$.bottles) } private$.bottles <- b})Beer$set("public", "drink", function() { if (self$bottles == 0) msg <- glue("No more bottles of {self$name} on the wall...\n") else { msg <- glue("{self$bottles} bottle{ifelse(self$bottles>1,'s','')} of {self$name}") msg <- glue("{msg} on the wall. {msg}! Take one down, pass it around.\n") self$bottles <- self$bottles - 1 } cli::cat_line(msg) invisible(self)})beer <- Beer$new("Duff")
## It's five o'clock somewhere. Give me a Duff!
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.
beer$drink()
## 6 bottles of Duff on the wall. 6 bottles of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.
beer$drink()
## 6 bottles of Duff on the wall. 6 bottles of Duff! Take one down, pass it around.
beer$drink()
## 5 bottles of Duff on the wall. 5 bottles of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.
beer$drink()
## 6 bottles of Duff on the wall. 6 bottles of Duff! Take one down, pass it around.
beer$drink()
## 5 bottles of Duff on the wall. 5 bottles of Duff! Take one down, pass it around.
beer$drink()
## 4 bottles of Duff on the wall. 4 bottles of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.
beer$drink()
## 6 bottles of Duff on the wall. 6 bottles of Duff! Take one down, pass it around.
beer$drink()
## 5 bottles of Duff on the wall. 5 bottles of Duff! Take one down, pass it around.
beer$drink()
## 4 bottles of Duff on the wall. 4 bottles of Duff! Take one down, pass it around.
beer$drink()
## 3 bottles of Duff on the wall. 3 bottles of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.
beer$drink()
## 6 bottles of Duff on the wall. 6 bottles of Duff! Take one down, pass it around.
beer$drink()
## 5 bottles of Duff on the wall. 5 bottles of Duff! Take one down, pass it around.
beer$drink()
## 4 bottles of Duff on the wall. 4 bottles of Duff! Take one down, pass it around.
beer$drink()
## 3 bottles of Duff on the wall. 3 bottles of Duff! Take one down, pass it around.
beer$drink()
## 2 bottles of Duff on the wall. 2 bottles of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.
beer$drink()
## 6 bottles of Duff on the wall. 6 bottles of Duff! Take one down, pass it around.
beer$drink()
## 5 bottles of Duff on the wall. 5 bottles of Duff! Take one down, pass it around.
beer$drink()
## 4 bottles of Duff on the wall. 4 bottles of Duff! Take one down, pass it around.
beer$drink()
## 3 bottles of Duff on the wall. 3 bottles of Duff! Take one down, pass it around.
beer$drink()
## 2 bottles of Duff on the wall. 2 bottles of Duff! Take one down, pass it around.
beer$drink()
## 1 bottle of Duff on the wall. 1 bottle of Duff! Take one down, pass it around.
beer$drink()
## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.
beer$drink()
## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.
beer$drink()
## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.
beer$drink()
## 6 bottles of Duff on the wall. 6 bottles of Duff! Take one down, pass it around.
beer$drink()
## 5 bottles of Duff on the wall. 5 bottles of Duff! Take one down, pass it around.
beer$drink()
## 4 bottles of Duff on the wall. 4 bottles of Duff! Take one down, pass it around.
beer$drink()
## 3 bottles of Duff on the wall. 3 bottles of Duff! Take one down, pass it around.
beer$drink()
## 2 bottles of Duff on the wall. 2 bottles of Duff! Take one down, pass it around.
beer$drink()
## 1 bottle of Duff on the wall. 1 bottle of Duff! Take one down, pass it around.
beer$drink()
## No more bottles of Duff on the wall...
If a method returns self
then you can chain calls together.
Beer$ new("Duff")$ drink()$ drink()$ drink()$ drink()$ drink()$ drink()$ drink()$ drink()$ drink()$ drink()
## It's five o'clock somewhere. Give me a Duff!## 9 bottles of Duff on the wall. 9 bottles of Duff! Take one down, pass it around.## 8 bottles of Duff on the wall. 8 bottles of Duff! Take one down, pass it around.## 7 bottles of Duff on the wall. 7 bottles of Duff! Take one down, pass it around.## 6 bottles of Duff on the wall. 6 bottles of Duff! Take one down, pass it around.## 5 bottles of Duff on the wall. 5 bottles of Duff! Take one down, pass it around.## 4 bottles of Duff on the wall. 4 bottles of Duff! Take one down, pass it around.## 3 bottles of Duff on the wall. 3 bottles of Duff! Take one down, pass it around.## 2 bottles of Duff on the wall. 2 bottles of Duff! Take one down, pass it around.## 1 bottle of Duff on the wall. 1 bottle of Duff! Take one down, pass it around.## No more bottles of Duff on the wall...
I had all intentions of demonstrating how R6 works under-the-hood using fancy diagrams like Hadley creates.
Instead, I will monkey around in R.
But, if you are interested and want to hurt your bra: https://rpubs.com/sumprain/r6
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |