Calling print(ls.str()) in function affect behavior of rep
Asked Answered
R

3

10

Begin a new R session with an empty environment. Write a series of functions with a parameter that is to be used as the value of the times parameter in a call to rep().

f <- function(n) {
  rep("hello", times = n)
}
f(x)

One expect this to fail, and indeed one gets:

# Error in f(x) : object 'x' not found

Modify the function a bit:

f2 <- function(n) {
  ls.str()
  rep("hello", times = n)
}

f2(x)

As expected, it still fails:

# Error in f2(x) : object 'x' not found

Modify a bit more (to see the environment in the console):

f3 <- function(n) {
  print(ls.str())
  rep("hello", times = n)
}

f3(x)

I still expect failure, but instead get:

## n : <missing>
## [1] "hello"

It is as if the call to print() makes rep work as though times were set to 1.

Rosemari answered 18/9, 2017 at 13:9 Comment(0)
R
6

I received earlier today a report that the bug has been fixed in R-devel and R-patched.

The issue was that the test for missingness in the R sources did not consider the case of an interrupted promise evaluation. A fix has been committed by Luke Tierney and can be seen on GitHub.

Rosemari answered 20/9, 2017 at 2:1 Comment(2)
Duh! Accepting now, (Still relatively new to Stack Exchange provedure ...)Rosemari
@BrodieG, no way was I gonna wade into the R-devel-list reporting a bug without confirming first with the Big Dogs. Hence my post here.Rosemari
C
6

This is not an answer, but too long to post as a comment. A minimal reproducible example is:

f3 <- function(n) {
  try(get("n", environment(), inherits=FALSE))
  rep("hello", times = n)
}
f3(x)
## Error in get("n", environment(), inherits = FALSE) : object 'x' not found
## [1] "hello"

The following is speculative and based on loosely examining the source for do_rep. get starts the promise evaluation, but upon not finding the "missing" symbol appears to leave the promise partially unevaluated. rep, being a primitive, then attempts to operate on n without realizing that it is a partially evaluated promise and basically that leads implicitly to the assumption that 'n == 1'.

Also, this shows that the promise is in a weird state (have to use browser/debug to see it):

f3a <- function(n) {
  try(get("n", environment(), inherits=FALSE))
  browser()
  rep("hello", times = n)
}
f3a(x)
## Error in get("n", environment(), inherits = FALSE) : object 'x' not found
## Called from: f3a(x)
# Browse[1]> (n)
## Error: object 'x' not found
## In addition: Warning message:
## restarting interrupted promise evaluation 
## Browse[1]> c
## [1] "hello"
Coppola answered 18/9, 2017 at 16:17 Comment(3)
I can already reproduce the code with only the rep line, no need for try(get(…)). In fact, I think do_rep simply does its manual argument matching wrong, bails out on a non-existent promise and pretends that no argument except x is set. The thing about the “partially unevaluated” promise is a red herring.Smackdab
@KonradRudolph I don't follow. f3a <- function(n) rep("hello", times = n); f3a(x) doesn't reproduce this.Southbound
@Southbound Argh, indeed. But f3a() does.Smackdab
R
6

I received earlier today a report that the bug has been fixed in R-devel and R-patched.

The issue was that the test for missingness in the R sources did not consider the case of an interrupted promise evaluation. A fix has been committed by Luke Tierney and can be seen on GitHub.

Rosemari answered 20/9, 2017 at 2:1 Comment(2)
Duh! Accepting now, (Still relatively new to Stack Exchange provedure ...)Rosemari
@BrodieG, no way was I gonna wade into the R-devel-list reporting a bug without confirming first with the Big Dogs. Hence my post here.Rosemari
S
2
f4 <- function(n) {
  print('test')
  print(ls.str())
  print('end test')
  rep("hello", times = n)
}
f4(x)

## [1] "test"
## n : <missing>
## [1] "end test"
## [1] "hello"

There's something within print.ls_str, from Frank's test on chat the follwing code exhibit the same problem:

f6 <- function(n) {
  z = tryCatch(get("n", new.env(), mode = "any"), error = function(e) e)
  rep("A", n)
}

Digging a little inside R source I found the following code

#     define GET_VALUE(rval)                      \
    /* We need to evaluate if it is a promise */  \ 
    if (TYPEOF(rval) == PROMSXP) {                \
        PROTECT(rval);                            \
        rval = eval(rval, genv);                  \
        UNPROTECT(1);                             \
    }                                             \
                                                  \
    if (!ISNULL(rval) && NAMED(rval) == 0)        \
        SET_NAMED(rval, 1)


    GET_VALUE(rval);
    break;


    case 2: // get0(.)
    if (rval == R_UnboundValue)
        return CAD4R(args);// i.e.  value_if_not_exists
    GET_VALUE(rval);
    break;
    }
    return rval;
}
#undef GET_VALUE

I'm quite surprised this compile properly, as far as I remember (my C is quite far behind) #define doesn't allow spaces between the # and define.

After digging for that, I'm wrong, from gcc doc:

Whitespace is also allowed before and after the `#'.

So there's probably something around this part of code, but that's above my head to pinpoint what exactly.

Shields answered 18/9, 2017 at 15:31 Comment(10)
I'm not sure what you mean by "empty" (NULL?), but I don't think it does anything like that. The reason is that f4 <- function(n) { print(ls.str()) substitute(n) } works as expected. For example f4(x) returns x.Rosemari
I think your attempt at an explanation is too simple. If I use f3 <- function(n) { print(ls.str()); print(n); rep("hello", times = n) } I get the interesting warning: In print(n) : restarting interrupted promise evaluation. So, apparently print.ls.str does something strange with the promise.Southbound
@Southbound indeed, digging a little more, as f6 <- function(n) { z = tryCatch(get("n", new.env(), mode = "any"), error = function(e) e); rep("A", n) } exhibit the same behavior (the tryCatch is coming from print.ls_str (thanks to Frank for the headup on this part)Shields
In any case, it's a bug and should be reported (probably to the R-devel mailing list).Southbound
@Southbound Did you do it?Smackdab
@KonradRudolph I've never posted on any mailing list and I don't plan to start now. It's very unfortunate that access to the R issue tracker had to be restricted.Southbound
@Southbound It seems that Homer White did: stat.ethz.ch/pipermail/r-devel/2017-September/074930.htmlSmackdab
Yes, the bug has been filed and fixed.Rosemari
@Homer You could self-answer and maybe change the title after the fix is in a stable release (which is what I did in a similar case #30036439 )Raeraeann
Thanks for the suggestion @Frank, I took you up on it.Rosemari

© 2022 - 2024 — McMap. All rights reserved.