Decorate a function or an existing method

Description

decorate() applies a decorator to a target: a function, or a method that already exists on an S3, S4, S7 or R6 class. Use %@% to decorate a function you are defining, and decorate() for code you did not write.

Usage

decorate(target, decorator, ...)

decorate(target, signature) <- value

Arguments

target What to decorate; see the table above.
decorator A decorator, or a stack of them built with %@%.
…

Arguments for specific kinds of target:

  • class: for S3, the class name as a string. For S7, the class or signature, as in S7::method(). For R6, the class generator.

  • signature: for S4, the signature, as in methods::setMethod().

  • envir: for S3, the environment to find the method and generic in. Defaults to the caller’s.

  • where: for S4, the environment to store the method in. Defaults to the top-level environment of the caller. For R6, the part of the class that holds the method: “public” (the default), “private” or “active”.

signature For the replacement form, the S7 class, S4 signature, S3 class name, or R6 method name(s), as above.
value For the replacement form, the decorator to apply.

Details

target Example Extra argument
a function decorate(fn, d), the same as d %@% fn
the name of an S3 generic decorate(“print”, d, class = “money”) class
an S4 generic decorate(area, d, signature = “Square”) signature
an S7 generic decorate(speak, d, class = Dog) class
the name of an R6 method decorate(“add”, d, class = Counter) class

For S3 and R6 the method is named by a string, and class gives the class or R6 generator. For S4 and S7 the generic is the target. The replacement form (see below) works for all four systems.

Value

For a function, the decorated function. For R6, the class generator, invisibly. For the others, the decorated method, invisibly; the method is replaced in place.

R6 classes

decorate(“name”, decorator, class = Generator) replaces the method in the generator, so objects created afterwards use it. Objects that already exist keep the old method, because R6 copies methods into each object when it is created. An inherited method is copied into Generator and decorated there; the parent class is unchanged. Give several names to decorate several methods.

Replacement form

decorate(target, class) <- decorator mirrors S7’s method(generic, class) <- fn and works for all four systems:

decorate(speak, Dog) <- shout        # S7
decorate(area, "Square") <- shout    # S4
decorate(print, "money") <- shout    # S3, with the generic function
decorate(Counter, "add") <- shout    # R6, public methods

R does not allow a string as the target of a replacement call, so for S3 pass the generic function. Its name is read from its UseMethod() call, so the generic must call UseMethod() directly. For R6 the form reaches public methods only; use the explicit form for where.

Examples

library("adorn")

# A function
shout <- decorator(function(f, ...) toupper(f(...)))
greet <- decorate(function(who) paste("hello", who), shout)
greet("world")
#> [1] "HELLO WORLD"

# An existing S3 method
area <- function(x, ...) UseMethod("area")
area.square <- function(x, ...) x$side^2
decorate("area", shout, class = "square")
area(structure(list(side = 3), class = "square"))
#> [1] "9"

# An R6 method
if (requireNamespace("R6", quietly = TRUE)) {
  Greeter <- R6::R6Class("Greeter", public = list(
    hi = function(who) paste("hello", who)
  ))
  decorate("hi", shout, class = Greeter)
  Greeter$new()$hi("world")
}
#> [1] "HELLO WORLD"