Skip to content

Commit

Permalink
Better names; start on docs
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley committed Dec 4, 2023
1 parent 1665d7b commit 20fd844
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 14 deletions.
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export("method<-")
export("prop<-")
export("props<-")
export(S4_register)
export(S7_Complex)
export(S7_Math)
export(S7_Ops)
export(S7_Summary)
export(S7_class)
export(S7_data)
export(S7_dispatch)
Expand Down
62 changes: 53 additions & 9 deletions R/method-group.R
Original file line number Diff line number Diff line change
@@ -1,34 +1,73 @@
group_generic_Math <- NULL
group_generic_Ops <- NULL
group_generic_Complex <- NULL
group_generic_Summary <- NULL
#' S7 Group Generics
#'
#' Group generics allow you to implement methods for many generics at once.
#' You cannot call a group generic directly; instead it is called automatically
#' by members of the group if a more specific method is not found. For example,
#' if you define a method for the `S7_Math` group generic, it will be called
#' when you call `abs()`, `sign()`, `sqrt()`, and many other similar generics
#' (see below for a complete list).
#'
#' @param x,z,e1,e2 Objects used for dispatch.
#' @param ...,na.rm Additional arguments passed to methods.
#' @param .Generic The name of the generic being dispatched on, i.e. if you've
#' defined a method for `S7_Math` and the user calls `abs()` then `.Generic`
#' will be `"abs"`.
#' @details
#' # Methods
#'
#' The group generics contain the following methods:
#'
#' * `Ops`: `r group_generics_md("Ops")`
#' * `Math`: `r group_generics_md("Math")`
#' * `Summary`: `r group_generics_md("Summary")`
#' * `Complex`: `r group_generics_md("Complex")`
#' * `matrixOps`: `r group_generics_md("matrixOps")`
#'
#' @name S7_group_generics
NULL

#' @export
#' @rdname S7_group_generics
S7_Math <- NULL

#' @export
#' @rdname S7_group_generics
S7_Ops <- NULL

#' @export
#' @rdname S7_group_generics
S7_Complex <- NULL

#' @export
#' @rdname S7_group_generics
S7_Summary <- NULL

on_load_define_group_generics <- function() {
group_generic_Math <<- new_generic(
S7_Math <<- new_generic(
"Math",
"x",
function(x, ..., .Generic) {
S7_dispatch()
}
)

group_generic_Ops <<- new_generic(
S7_Ops <<- new_generic(
"Ops",
c("e1", "e2"),
function(e1, e2, ..., .Generic) {
S7_dispatch()
}
)

group_generic_Complex <<- new_generic(
S7_Complex <<- new_generic(
"Complex",
"z",
function(z, ..., .Generic) {
S7_dispatch()
}
)

group_generic_Summary <<- new_generic(
S7_Summary <<- new_generic(
"Summary",
"x",
function(x, ..., na.rm = FALSE, .Generic) {
Expand All @@ -41,9 +80,14 @@ on_load_define_group_generics <- function() {
Math.S7_object <- function(x, ...) {
generic_fun <- get(.Generic, mode = "function", envir = baseenv())
tryCatch(
return(group_generic_Math(x, ..., .Generic = generic_fun)),
return(S7_Math(x, ..., .Generic = generic_fun)),
S7_error_method_not_found = function(cnd) NULL
)

NextMethod()
}


group_generics_md <- function(name) {
paste0("`", group_generics()[[name]], "`", collapse = ", ")
}
2 changes: 1 addition & 1 deletion R/method-ops.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Ops.S7_object <- function(e1, e2) {
# Try group generic
generic_fun <- get(.Generic, mode = "function", envir = baseenv())
cnd <- tryCatch(
return(group_generic_Ops(e1, e2, .Generic = generic_fun)),
return(S7_Ops(e1, e2, .Generic = generic_fun)),
S7_error_method_not_found = function(cnd) cnd
)

Expand Down
46 changes: 46 additions & 0 deletions man/S7_group_generics.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions tests/testthat/test-method-group.R
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
test_that("can provide Math group generic", {
local_methods(group_generic_Math)
local_methods(S7_Math)
foo1 <- new_class("foo1", properties = list(x = class_double, y = class_double))
foo2 <- new_class("foo2", class_double)

# base behaviour
expect_snapshot(abs(foo1(-1, 2)), error = TRUE)
expect_equal(abs(foo2(c(-1, 2))), foo2(c(1, 2)))

method(group_generic_Math, foo1) <- function(x, ..., .Generic) {
method(S7_Math, foo1) <- function(x, ..., .Generic) {
foo1(.Generic(x@x, ...), .Generic(x@y, ...))
}
expect_equal(abs(foo1(-1, 2)), foo1(1, 2))

method(group_generic_Math, foo2) <- function(x, ..., .Generic) {
method(S7_Math, foo2) <- function(x, ..., .Generic) {
foo2(.Generic(S7_data(x, ...)))
}
expect_equal(abs(foo2(c(-1, 2))), foo2(c(1, 2)))
Expand Down
4 changes: 3 additions & 1 deletion tests/testthat/test-method-ops.R
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,14 @@ test_that("Ops generics falls back to base behaviour", {
})

test_that("specific method overrides group generic", {
local_methods(base_ops[["+"]], S7_Ops)

foo <- new_class("foo", class_integer)

method(`+`, list(foo, foo)) <- function(e1, e2) {
foo(S7_data(e1) + S7_data(e2) + 100L)
}
method(group_generic_Ops, list(foo, foo)) <- function(e1, e2, .Generic) {
method(S7_Ops, list(foo, foo)) <- function(e1, e2, .Generic) {
foo(.Generic(S7_data(e1), S7_data(e2)))
}

Expand Down

0 comments on commit 20fd844

Please sign in to comment.