Skip to content

Lexical scoping/calling stack issue: R fails to recognize an argument's default value

4 messages · Jeff Newmiller, Janko Thyson

#
Dear list,

I just encountered a behavior that I've never seen before:

Is it possible, that certain argument names (lazy in my case) are
special/reserved and thus would lead to unexpected behavior when a calling
stack is involved that spreads across functions of three different
packages: optionr::setAnywhereOptions() calls nestr::setNested() calls
reactr::setShinyReactive()?

Or is there something I'm generally missing with respect the combination of
lexical scoping/the frame stack, S4 and default values of function
arguments.

Running the following code leads to a situation where the default value of
`lazy` in `reactr::setShinyReactive()` is not properly recognized while
others (e.g. `push`) are recognized just fine:

require("devtools")
devtools::install_github("Rappster/conditionr")
devtools::install_github("Rappster/nestr")
devtools::install_github("Rappster/optionr")
require("optionr")

container <- initializeOptionContainer(overwrite = TRUE)
expect_true(setAnywhereOption(id = "x_1", value = TRUE, reactive = TRUE))
expect_equal(getAnywhereOption(id = "x_1"), TRUE)
expect_true(res <- setAnywhereOption(id = "x_2",
  value = reactr::reactiveExpression(
    !getAnywhereOption(id = "x_1")
  ),
  reactive = TRUE))

The current version of `setShinyReactive()` contains a debugging section
that prints these status messages (the actual code:
https://github.com/Rappster/reactr/blob/bug-28/R/setShinyReactive.r#L196)

DEBUG/push/before[1] FALSE
DEBUG/lazy/before
Error in print(lazy) : argument is missing, with no default
DEBUG/is_lazy/before[1] FALSE
DEBUG/lazy/after[1] FALSE

It also contains my current workaround: also include an argument with
name `is_lazy` (whose default)
value is recognized again) and then run `lazy <- is_lazy`.

You can also find this information in this Stackoverflow post:
http://stackoverflow.com/questions/26940474/lexical-scoping-issue-r-fails-to-recognize-an-arguments-default-value

Thanks a lot for everyone that can shed some light on this!


Best regards,
Janko
#
While you appear to have been thorough in providing access to your code, I don't think I will install a bunch of your dev code to debug it for you. The Posting Guide does say your example should be minimal, and IMO this doesn't fit that description. You should extract enough generic functions to replicate the structure of the call tree in a single short example.

I suppose there could be a bug in the parameter handling, but are you sure every calling point is including the "..." argument that should be?

My second blind point is that visibility of package variables does follow a complicated path, but there are good descriptions available, such as [1] or [2].

If you do think you have found a bug, the R Core team will want a minimal reproducible example, and you should read the Posting Guide on bug reporting to make sure your issue gets addressed.

[1] http://obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/
[2] http://adv-r.had.co.nz/Environments.html
---------------------------------------------------------------------------
Jeff Newmiller                        The     .....       .....  Go Live...
DCN:<jdnewmil at dcn.davis.ca.us>        Basics: ##.#.       ##.#.  Live Go...
                                      Live:   OO#.. Dead: OO#..  Playing
Research Engineer (Solar/Batteries            O.O#.       #.O#.  with
/Software/Embedded Controllers)               .OO#.       .OO#.  rocks...1k
--------------------------------------------------------------------------- 
Sent from my phone. Please excuse my brevity.
On November 14, 2014 3:51:16 PM PST, Janko Thyson <janko.thyson at gmail.com> wrote:
#
Thanks. I will try to further simplify the example.

On Sat, Nov 15, 2014 at 2:01 AM, Jeff Newmiller <jdnewmil at dcn.davis.ca.us>
wrote:

  
  
#
Ok, I have to admit: that was a really stupid mistake :-/

I unintentionally had a trailing `,` in the call to `nestr::setNested()`
inside `optionr::setAnywhereOption()`

Here's a much simpler illustration:

*Definitions* //

    setGeneric(
      name = "setAnywhereOption",
      signature = "id",
      def = function(id, ...) standardGeneric("setAnywhereOption")
    )
    setMethod(
      f = "setAnywhereOption",
      signature = signature(id = "character"),
    #  definition = function(id, ...) setNested(id = id)
      ## --> works
    #  definition = function(id, ...) setNested(id = id, ...)
      ## --> works
      definition = function(id, ...) setNested(id = id,)
      ## --> this leads to things get messed up with argument's default
values
      ## --> so the trailing `,` was causing the problem!
    )
    setGeneric(
      name = "setNested",
      signature = "id",
      def = function(id, ...) standardGeneric("setNested")
    )
    setMethod(
      f = "setNested",
      signature = signature(id = "character"),
      definition = function(id, ...) {

      if (FALSE) {
        ## Omitted
      } else {
        setShinyReactive(id = basename(id), ...)
      }

    })
    setShinyReactive <- function(
      id,
      lazy = FALSE,
      is_lazy = FALSE,
      push = FALSE,
      typed = FALSE,
      strict_set = c(0, 1, 2),
      ...
      ) {

      ###########
      ## DEBUG ##
      ###########
      message("DEBUG/setShinyReactive/threedots")
      print(list(...))
      message("DEBUG/setShinyReactive/push")
      print(push)
      message("DEBUG/setShinyReactive/lazy")
      try(print(lazy))
      ## --> strangely, R does not seem to like the name `lazy`
      message("DEBUG/setShinyReactive/is_lazy")
      print(is_lazy)
      ## --> this works
      lazy <- is_lazy
      message("DEBUG/setShinyReactive/lazy")
      print(lazy)

      TRUE

    }

*Apply //*

    setAnywhereOption(id = "test")
    # DEBUG/setShinyReactive/threedots
    # list()
    # DEBUG/setShinyReactive/push
    # [1] FALSE
    # DEBUG/setShinyReactive/lazy
    # Error in print(lazy) : argument is missing, with no default
    # DEBUG/setShinyReactive/is_lazy
    # [1] FALSE
    # DEBUG/setShinyReactive/lazy
    # [1] FALSE
    # [1] TRUE
    setAnywhereOption(id = "test", push = TRUE)
    setAnywhereOption(id = "test", lazy = TRUE)

*Actual cause / solution //*

Removing the trailing `,` in the method definition of `setAnywhereOption()`:

    setMethod(
      f = "setAnywhereOption",
      signature = signature(id = "character"),
      definition = function(id, ...) setNested(id = id)
      ## --> works
    #  definition = function(id, ...) setNested(id = id,)
      ## --> this leads to things get messed up with argument's default
values
      ## --> so the trailing `,` was causing the problem!
    )

    setAnywhereOption(id = "test")
    # DEBUG/setShinyReactive/threedots
    # list()
    # DEBUG/setShinyReactive/push
    # [1] FALSE
    # DEBUG/setShinyReactive/lazy
    # [1] FALSE
    # DEBUG/setShinyReactive/is_lazy
    # [1] FALSE
    # DEBUG/setShinyReactive/lazy
    # [1] FALSE
    # [1] TRUE

Now it works just fine. Also stated this as an answer as potential
reference for others that might run into similar problems:
http://stackoverflow.com/questions/26940474/lexical-scoping-calling-stack-issue-r-fails-to-recognize-an-arguments-defaul

Thanks for everyone that answered/took the time to have a look at the code
- and sorry for having thrown a complicated example at you!
It was just that at first I didn't see the wood for the trees and I thought
that the problem was caused by this very calling stack structure (spread
across three different packages).

Best regards and happy coding,
Janko

On Sat, Nov 15, 2014 at 2:06 AM, Janko Thyson <janko.thyson at gmail.com>
wrote: