Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R pattern to modify arguments passed to generic function

Tags:

oop

r

I have a generic function called foo. It operates differently on the classes bar and baz but there is some shared pre-processing that needs to only be done once. What is the idiomatic R way of carrying this out?

In my silly example below, I want to multiply the object being passed to the generic by 10. Yet when the method is called, the original value is printed to the console. I have read the language definition and know that the below pattern does not work. My question is: Where or how should I handle shared processing of generic arguments before calling the method?

a <- structure(1:5, class="bar")
b <- structure(6:10, class="baz")

foo <- function(x) {
  x <- x * 10 # where should shared preprocessing go?
  UseMethod("foo")
}

foo.bar <- function(x) {
  cat("Foo!", x)
}

foo.baz <- function(x) {
  cat("Baz!", x)
}

# does not propagate the operations carried out `foo`
foo(a)
foo(b)
like image 417
Zelazny7 Avatar asked Dec 29 '15 14:12

Zelazny7


1 Answers

1) Layer foo on top of actual generic Change foo to perform the preliminary code and invoke foo_ a new generic as shown. Rename foo.bar and foo.baz to foo_.bar and foo_.baz respectively so that we are left with (have also added newlines to the example):

foo <- function(x) {
    x <- x * 10
    foo_(x)
}

foo_ <- function(x) UseMethod("foo_")

foo_.bar <- function(x) cat("Foo!", x, "\n")
foo_.baz <- function(x) cat("Baz!", x, "\n")

Now test it out:

a <- structure(1:5, class="bar")
b <- structure(6:10, class="baz")

foo(a)
## Foo! 10 20 30 40 50
foo(b)
## Baz! 60 70 80 90 100

For an example of this in a widely used package see the source of dplyr::mutate

2) NextMethod Another way would be to give every object a class vector of two classes with "foo" made a subclass of "bar" in the case of a and of "baz" in the case of b. Then use NextMethod. Solution (1) seems simpler and it may seem weird that "foo" is a subclass of both "bar" and "baz" but here is an example of this one just in case:

foo <- function(x) UseMethod("foo")

foo.foo <- function(x) {
    x <- x * 10
    NextMethod()
}
foo.bar <- function(x) cat("Foo!", x, "\n")
foo.baz <- function(x) cat("Baz!", x, "\n")

Test it noting that we have changed the definitions of a and b so that they work with this approach:

a <- structure(1:5, class= c("foo", "bar"))
b <- structure(6:10, class = c("foo", "baz"))

foo(a)
## Foo! 10 20 30 40 50
foo(b)
## Baz! 60 70 80 90 100
like image 119
G. Grothendieck Avatar answered Dec 06 '22 01:12

G. Grothendieck