1

I have been looking for a way to do object destructuring. This in an effort to write cleaner code when developing packages.

I often have a complex system of functions that call others etc and I want to make those arguments available at the higher level functions to the user.

When developing packages for R I end up often writing functions that call other functions and end up having to either use the ... operator (which can only be used once. Or to manually re-assign the object like so:

someFunction1 <- function(arg1, arg2) { print(stringr::str_interp('Do something with ${arg1}, and with ${arg2}')) }

someFunction2 <- function(arg3, arg4) { print(stringr::str_interp('Do something with ${arg1}, and with ${arg2}')) }

# solution with R's ...

someHigherFunction <- function(arg5, arg6, ...) {
    # do something with arg5 and 6

    someFunction1(...)
}

# otherwise I start having to do this; and the more nested the functions get the harder it is to manage
someHigherFunction <- function(arg5, arg6, arg1, arg2, arg3, arg4) {
    # do something with arg5 and 6

    someFunction1(arg1 = arg1, arg2 = arg2)
    someFunction1(arg3 = arg3, arg4 = arg4)
}

# there is a solution using do.call but it is rather annoying

someHigherFunction <- function(arg5, arg6, arg1_2 = list(arg1 = "something", arg2 = "something else")) {
    # do something with arg5 and 6

    # not ideal but it works
    do.call(someFunction1, arg1_2)
}

In JavaScript we can use object destructuring (easy for positional arguments), python has this too with it's own ** double star operator:

function foo(a, b) {
    console.log(a - b);
}

let args = [10, 7];

foo(...args);

There are other uses for this as well:

let a, b, rest;
[a, b] = [10, 20];

console.log(a);
// expected output: 10

console.log(b);
// expected output: 20

[a, b, ...rest] = [10, 20, 30, 40, 50];

console.log(rest);
// expected output: Array [30,40,50]

Does anyone have any suggestions on how to accomplish this in R? Did I miss something?

  • Can you be clearer about what behaviour you are trying to achieve? Depending on what your use case is the [!!!](https://rlang.r-lib.org/reference/splice-operator.html) operator from the rlang package or `match.call(expand.dots = FALSE)$...` may be useful. – MorganK95 Jun 22 '22 at 04:51
  • 1
    Hi thank you, yes I will try to boil it down to one sentence. I want to be able to do this: ```r someHigherFunction(arg5, arg6, ...) { someFunction1(...); someFunction2(...) } ``` This would facilitate exposing arguments from lower functions to the user at the higher level. However, this code I show above throws an error, as the arguments passed after arg5/arg6 are passed to both lower functions. – dereckmezquita Jun 22 '22 at 17:08

1 Answers1

1

Expanding on above, is this pattern useful/generalizable?

someHigherFunction4 <- function(arg5, arg6, ...) {
  
  # collect arguments passed in dots
  
  args <- match.call(expand.dots = FALSE)$...
  
  # if arguments named, subset dots to avoid unused variable error in do.call
  
  if(!is.null(names(args))) {
    
    validArgs <- formalArgs(someFunction1)
    
    do.call(someFunction1, args[validArgs])
    
  } else {
    
  # if not, pass arguments by position
    
  do.call(someFunction1, as.list(args))

  }
}

someHigherFunction4("foo", TRUE, "this", "that")
# result: "Do something with this, and with that"

someHigherFunction4("foo", TRUE, arg1 = "this", arg2 = "that", arg3 = "bar")
# result: "Do something with this, and with that"
MorganK95
  • 137
  • 5
  • Thank you for that. Do you have a way that we could call multiple lower functions from the higher one and split up the extra arguments passed to the lower functions? This doesn't work for example: ```r someHigherFunction(arg5, arg6, ...) { someFunction1(...) someFunction2(...) } ``` This throws a unknown argument error for respective functions. – dereckmezquita Jun 22 '22 at 17:04
  • I'm not sure there is a good way to do this. After a quick search, I found two related questions which may be of use: [Split up `...` arguments](https://stackoverflow.com/questions/25376197/split-up-arguments-and-distribute-to-multiple-functions) and [How to pass parameters to child functions](https://stackoverflow.com/questions/25749661/how-to-pass-the-parameters-in-the-parent-function-to-its-two-children-func). – MorganK95 Jun 22 '22 at 23:25
  • Not sure where you ended up on this, but by chance I stumbled on the [zeallot](https://github.com/r-lib/zeallot) package today. Looks like it might work. – MorganK95 Jul 27 '22 at 01:53