Skip to content

Commit defec3f

Browse files
authored
Export, document, and use := (#658)
`foo := bar()` is a shorthand for `foo <- bar(name = "foo")`, thus it's a convenient syntax for use this `new_class()` and `new_generic()`. We now use it in all tests and public facing documentation, while reserving the right to change the implementation in the future. Fixes #466
1 parent b2b4b60 commit defec3f

87 files changed

Lines changed: 619 additions & 525 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ S3method(str,S7_object)
3232
S3method(str,S7_property)
3333
S3method(str,S7_super)
3434
S3method(str,S7_union)
35+
export(":=")
3536
export("S7_data<-")
3637
export("method<-")
3738
export("prop<-")

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# S7 (development version)
22

3+
* New `:=` operator creates and names an object in one step, so `Foo := new_class()` is equivalent to `Foo <- new_class(name = "Foo")` (#658).
34
* Errors thrown by S7 now report the function where they occurred, making it easier to track down the source of a problem (#646).
45
* `class_POSIXct` uses the `tzone` attribute (not `tz`), and allows it to be absent (#401).
56
* Base type wrappers like `class_integer` now define their constructor and validator in the S7 namespace. (#553).

R/S3.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
#' # No checking, just used for dispatch
8585
#' Date <- new_S3_class("Date")
8686
#'
87-
#' my_generic <- new_generic("my_generic", "x")
87+
#' my_generic := new_generic("x")
8888
#' method(my_generic, Date) <- function(x) "This is a date"
8989
#'
9090
#' my_generic(Sys.Date())

R/S4.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#' standardGeneric("S4_generic")
1515
#' })
1616
#'
17-
#' Foo <- new_class("Foo")
17+
#' Foo := new_class()
1818
#' S4_register(Foo)
1919
#' method(S4_generic, Foo) <- function(x) "Hello"
2020
#'

R/base-environment.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#'
2121
#' @export
2222
#' @examples
23-
#' Counter <- new_class("Counter", class_environment)
23+
#' Counter := new_class(class_environment)
2424
#' counter <- Counter()
2525
#' counter$n <- 0L
2626
#'

R/bind.R

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#' Create and name an object in one step
2+
#'
3+
#' @description
4+
#' Functions like [new_class()] and [new_generic()] take a `name` that, by
5+
#' convention, matches the name of the variable that you assign their result
6+
#' to. The `:=` operator eliminates this duplication: `Foo := new_class()` is
7+
#' equivalent to `Foo <- new_class(name = "Foo")`.
8+
#'
9+
#' `:=` works with any function that has a `name` argument, but bear in mind
10+
#' that it adds `name` to the call as a named argument, so any other arguments
11+
#' supplied positionally will shift to fill the remaining parameters.
12+
#'
13+
#' @usage lhs := rhs
14+
#' @param lhs A bare symbol: both the name of the variable to create and the
15+
#' `name` supplied to the right-hand side.
16+
#' @param rhs A call to a function with a `name` argument.
17+
#' @return The result of evaluating `rhs`, which is also assigned to `lhs` in
18+
#' the calling environment, returned invisibly.
19+
#' @export
20+
#' @rdname named-bind
21+
#' @examples
22+
#' Range := new_class(properties = list(
23+
#' start = class_double,
24+
#' end = class_double
25+
#' ))
26+
#' Range
27+
#'
28+
#' describe := new_generic("x")
29+
#' describe
30+
`:=` <- function(lhs, rhs) {
31+
cl <- sys.call()
32+
if (!is.symbol(cl[[2L]])) {
33+
stop2("Left-hand side of `:=` must be a symbol.")
34+
}
35+
if (!is.call(cl[[3L]])) {
36+
stop2("Right-hand side of `:=` must be a function call.")
37+
}
38+
if ("name" %in% names(cl[[3L]])) {
39+
stop2("Right-hand side of `:=` must not already supply a `name` argument.")
40+
}
41+
42+
cl[[1L]] <- quote(`<-`)
43+
cl[[3L]]$name <- as.character(cl[[2L]])
44+
invisible(eval.parent(cl))
45+
}

R/class.R

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
#' CamelCase for S7 class names, but it is not required.)
1212
#'
1313
#' The result of calling `new_class()` should always be assigned to a variable
14-
#' with this name, i.e. `Foo <- new_class("Foo")`. This object both represents
15-
#' the class and is used to construct new instances of the class.
14+
#' with this name, i.e. `Foo <- new_class("Foo", ...)` or
15+
#' `Foo := new_class(...)`. This object both represents the class and is used
16+
#' to construct new instances of the class.
1617
#' @param parent The parent class to inherit behavior from.
1718
#' There are three options:
1819
#'
@@ -59,7 +60,7 @@
5960
#' @export
6061
#' @examples
6162
#' # Create an class that represents a range using a numeric start and end
62-
#' Range <- new_class("Range",
63+
#' Range := new_class(
6364
#' properties = list(
6465
#' start = class_numeric,
6566
#' end = class_numeric
@@ -77,7 +78,7 @@
7778
#'
7879
#' # But we might also want to use a validator to ensure that start and end
7980
#' # are length 1, and that start is < end
80-
#' Range <- new_class("Range",
81+
#' Range := new_class(
8182
#' properties = list(
8283
#' start = class_numeric,
8384
#' end = class_numeric
@@ -397,7 +398,7 @@ str.S7_object <- function(object, ..., nest.lev = 0) {
397398
#' @returns A class specification.
398399
#' @export
399400
#' @examples
400-
#' Foo <- new_class("Foo")
401+
#' Foo := new_class()
401402
#' S7_class(Foo())
402403
#'
403404
#' # Also works on non-S7 objects

R/convert.R

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@
5454
#' is not possible.
5555
#' @export
5656
#' @examples
57-
#' Foo1 <- new_class("Foo1", properties = list(x = class_integer))
58-
#' Foo2 <- new_class("Foo2", Foo1, properties = list(y = class_double))
57+
#' Foo1 := new_class(properties = list(x = class_integer))
58+
#' Foo2 := new_class(Foo1, properties = list(y = class_double))
5959
#'
6060
#' # Upcasting: S7 provides a default implementation for coercing an object
6161
#' # to one of its parent classes:
@@ -94,9 +94,9 @@
9494
#' # Conversely, `convert()` *does* use inheritance for `from`, so a method
9595
#' # registered on a parent class is also used for its children. This holds
9696
#' # even when upcasting, where it overrides the default property stripping:
97-
#' Bar1 <- new_class("Bar1", properties = list(label = class_character))
98-
#' Bar2 <- new_class("Bar2", Bar1)
99-
#' Bar3 <- new_class("Bar3", Bar2)
97+
#' Bar1 := new_class(properties = list(label = class_character))
98+
#' Bar2 := new_class(Bar1)
99+
#' Bar3 := new_class(Bar2)
100100
#' method(convert, list(Bar2, Bar1)) <- function(from, to, ...) {
101101
#' Bar1(label = "from a Bar2 or one of its children")
102102
#' }
@@ -106,7 +106,7 @@
106106
#' # This `from`-inheritance is limited to classes more specific than `to`. A
107107
#' # method whose `from` is a *parent* of `to` would downcast, so it is skipped.
108108
#' # For example, this method downcasts a Foo1 to a Foo2:
109-
#' Foo3 <- new_class("Foo3", Foo2, properties = list(z = class_double))
109+
#' Foo3 := new_class(Foo2, properties = list(z = class_double))
110110
#' method(convert, list(Foo1, Foo2)) <- function(from, to, ...) Foo2(y = -1)
111111
#'
112112
#' # Upcasting a Foo3 to a Foo2 ignores that inherited downcasting method,

R/data.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#' invisibly.
1414
#' @export
1515
#' @examples
16-
#' Text <- new_class("Text", parent = class_character)
16+
#' Text := new_class(parent = class_character)
1717
#' y <- Text(c(foo = "bar"))
1818
#' y
1919
#' S7_data(y)
@@ -22,7 +22,7 @@
2222
#' y
2323
#'
2424
#' # S3 classes are preserved
25-
#' MyDF <- new_class("MyDF", parent = class_data.frame)
25+
#' MyDF := new_class(parent = class_data.frame)
2626
#' S7_data(MyDF(data.frame(x = 1, y = 2)))
2727
S7_data <- function(object) {
2828
check_is_S7(object)

R/external-generic.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#' `S7_external_generic`.
2525
#' @export
2626
#' @examples
27-
#' MyClass <- new_class("MyClass")
27+
#' MyClass := new_class()
2828
#'
2929
#' your_generic <- new_external_generic("stats", "median", "x")
3030
#' method(your_generic, MyClass) <- function(x) "Hi!"

0 commit comments

Comments
 (0)