Package 'adorn'

Title: A Framework for Writing and Applying Function Decorators
Description: A framework for creating function decorators in R. Decorators can be applied to functions and to S3, S4, S7 and R6 methods.
Authors: Dyfan Jones [aut, cre]
Maintainer: Dyfan Jones <[email protected]>
License: MIT + file LICENSE
Version: 0.0.0.9000
Built: 2026-10-26 19:54:54 UTC
Source: https://github.com/DyfanJones/adorn

Help Index


Apply decorators to a function

Description

⁠%@%⁠ reads left to right: in a %@% b %@% f, a is the outermost decorator and f the innermost function. A plain ⁠function(f)⁠ that returns a function also works as a decorator on the left-hand side.

Usage

lhs %@% rhs

Arguments

lhs

A decorator.

rhs

A function, or another decorator to compose with.

Value

The decorated function, or a composed decorator if rhs is itself a decorator.

Examples

loud <- decorator(function(f, ...) toupper(f(...))) %@% function() "hi"
loud()

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

# A function
shout <- decorator(function(f, ...) toupper(f(...)))
greet <- decorate(function(who) paste("hello", who), shout)
greet("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"))

# 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")
}

Create a decorator

Description

decorator() turns an ordinary function into a decorator. The first argument of wrapper receives the function being decorated. There are two styles, chosen by whether wrapper has a ... argument:

Usage

decorator(wrapper, name = NULL)

Arguments

wrapper

A function, as described above.

name

Name shown when printing the decorator and in decorators(). By default it is taken from how the decorator is written when applied with ⁠%@%⁠ (timed %@% f gives "timed", and retry(times = 5) %@% f gives "retry"). Set it to override that; "decorator" is the last resort.

Details

  • Call style, ⁠function(f, ..., <params>)⁠: the body runs on every call. ... holds the arguments of that call; forward them with f(...).

  • Factory style, ⁠function(f, <params>)⁠: the body runs once, when the function is decorated, and returns the replacement function. Use this when the decorator needs state, such as a cache.

Arguments after ... (call style) or after f (factory style) are decorator parameters. Set them by calling the decorator: retry(times = 5), for a decorator named retry.

If your decorator stores the result of f(...) before returning it, use withVisible() to keep functions that return invisible() quiet.

Whatever the style, the decorated function keeps the formal arguments of the original, so argument matching, missing(), defaults and introspection all behave as before.

Value

A decorator: a function that decorates a function when called with one, and returns a configured decorator when called with parameters.

Examples

# Call style: runs on every call
shout <- decorator(function(f, ...) toupper(f(...)))

# Call style with a parameter
repeated <- decorator(function(f, ..., times = 2) {
  rep(f(...), times)
})

# Factory style: keeps state between calls
counted <- decorator(function(f) {
  n <- 0
  function(...) {
    n <<- n + 1
    message("call ", n)
    f(...)
  }
})

greet <- shout %@% function(who) paste("hello", who)
greet("world")

ones <- repeated(times = 3) %@% function() 1
ones()

Information about the function being decorated

Description

Call this from inside a decorator to find out which function it is currently wrapping.

Usage

decorator_context()

Value

A list with name (the name the function is bound to, or "<anonymous>"), decorator (the decorator's name, see decorator()) and fn (the function being wrapped).

Examples

who <- decorator(function(f, ...) {
  message("calling ", decorator_context()$name)
  f(...)
})
hello <- who %@% function() "hi"
hello()

Inspect a decorated function

Description

layers() is an alias for decorators(), and strip() for undecorate().

Usage

undecorate(f)

decorators(f)

Arguments

f

A function.

Value

undecorate() returns the original, undecorated function (or f itself if it is not decorated). decorators() returns the names of the decorators applied, outermost first.