There are languages which allow to have a trailing comma, for example after the last item in a list: https://www.jetbrains.com/help/rider/Trailing_Commas_Style.html. In R however, this is most of the time not the case:

lst <- list(
  a = 1, # notice the faulty comma here
)
# Error in list(a = 1, ) : argument 2 is empty

Things get a bit more complicated in when calling functions with ellipsis arguments:

fn_with_ellipsis <- function(x, ...)
{
  message("x: ", x)
}

# this triggers NO error
fn_with_ellipsis(x = 42, )
# x: 42

What triggered this blog entry is the following example:

# this is ok, gives the error we expect
rlang::abort(
  message = "leaving this world",
  class = "example_error_class"
)
# Error:
#   ! leaving this world
# Run `rlang::last_error()` to see where the error occurred.

Recently, we switched rlang version 1.0.1. As a result, the following code triggered an error:

rlang::abort(
  message = "leaving this world",
  class = "example_error_class", # notice the faulty comma here
)
# Error in `list2()`:
#   ! Argument 3 is empty
# Run `rlang::last_error()` to see where the error occurred.

So I had to do a hot fix only for one comma...

The interesting part is: how can I actually steer the response to trailing commas in the context of ellipsis. The rlang::dots_list function helps with this:

fn_with_ellipsis <- function(x, ...)
{
  message("x: ", x)
  
  rlang::dots_list(..., .ignore_empty = "trailing")
}
fn_with_ellipsis(x = 42, )
# x: 42
# named list()

fn_with_ellipsis <- function(x, ...)
{
  message("x: ", x)
  
  rlang::dots_list(..., .ignore_empty = "none")
}
fn_with_ellipsis(x = 42, )
# x: 42
# Error in `rlang::dots_list()`:
#   ! Argument 1 is empty
# Run `rlang::last_error()` to see where the error occurred.

 

I often have trailing commas issues when writing R6 classes.

ExampleClass <- R6::R6Class(
  public = list(
    initialize = function(){},  # notice the faulty comma here
  ),
  private = list()
)
# Error in list(initialize = function() { : argument 2 is empty

The problem might seem obvious here, but in practice the classes contain a lot of code and knowing what this error message means narrows down the search.

For completeness, here is another error that I met some days ago, while refactoring some code into R6 classes:

ExampleClass <- R6::R6Class(
  public = list(
    initialize = function(){
      self$public_member = 42
    }
  ),
  private = list()
)
obj <- ExampleClass$new()
# Error in self$public_member = 42 : 
#   cannot add bindings to a locked environment

Reason: forgotten declaration of public member (see below). Note: the same error is issued in case of private members.

ExampleClass <- R6::R6Class(
  public = list(
    public_member = NULL, # this was missing above
    initialize = function(){
      self$public_member = 42
    }
  ),
  private = list()
)
obj <- ExampleClass$new()
# no error

ExampleClass <- R6::R6Class(
  public = list(
    public_member = NULL, # this was missing above
    initialize = function(){
      self$public_member = 42
      private$private_member2 = "foo"
    }
  ),
  private = list()
)
obj <- ExampleClass$new()
# Error in private$private_member2 = "foo" : object 'private' not found

ExampleClass <- R6::R6Class(
  public = list(
    public_member = NULL, # this was missing above
    initialize = function(){
      self$public_member = 42
      private$private_member2 = "foo"
    }
  ),
  private = list(
    private_member1 = NULL
  )
)
obj <- ExampleClass$new()
# Error in private$private_member2 = "foo" : 
# cannot add bindings to a locked environment

ExampleClass <- R6::R6Class(
  public = list(
    public_member = NULL, # this was missing above
    initialize = function(){
      self$public_member = 42
      private$private_member2 = "foo"
    }
  ),
  private = list(
    private_member1 = NULL,
    private_member2 = NULL  # this was missing above
  )
)
obj <- ExampleClass$new()
# ok, no error

 

https://xkcd.com/1345/

xkcd-answers

 

Just in case you don't need as much sleeping time as me, and if you feel curious enough, here is a link to A reading club for software developers which I highly recommend.

Skimming through the articles posted there, here some highlights:

 

Make a promise. Show up. Do the work. Repeat.