topenv of emptyenv

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

topenv of emptyenv

Konrad Rudolph-2
I was surprised just now to find out that `topenv(emptyenv())` equals
… `.GlobalEnv`, not `emptyenv()`. From my understanding of the
description of `topenv`, it should walk up the chain of enclosing
environments (as if by calling `e = parent.env(e)` repeatedly; in
fact, that is almost exactly its implementation in envir.c) until it
hits a top level. However, `emptyenv()` has no enclosing environments
so it should be its own top-level environment (I thought).
Unfortunately the documentation on environments is relatively sparse,
and the R Internals document doesn’t mention top-level environments.

Concretely, I encountered this in the following code, which signals an
error if `env` is the empty environment:

while (! some_complex_condition(env) && ! identical(env, toplevel(env))) {
    env = parent.env(env)
}

Of course there’s a trivial workaround (add an identity check for
`emptyenv()` in the while loop condition) but it got me wondering if
there’s a rationale for this result or if it’s “accidental”/arbitrary:
the C `topenv` implementation defaults to returning R_GlobalEnv for an
empty environment. Is this effect actually useful (and used anywhere)?

This is in R 3.4.4 but I can’t find an indication that this behaviour
was ever changed.

Cheers

--
Konrad Rudolph

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: topenv of emptyenv

Martin Maechler
>>>>> Konrad Rudolph
>>>>>     on Sat, 23 Mar 2019 14:26:40 +0000 writes:
>>>>> Konrad Rudolph
>>>>>     on Sat, 23 Mar 2019 14:26:40 +0000 writes:

    > I was surprised just now to find out that `topenv(emptyenv())` equals
    > … `.GlobalEnv`, not `emptyenv()`. From my understanding of the
    > description of `topenv`, it should walk up the chain of enclosing
    > environments (as if by calling `e = parent.env(e)` repeatedly; in
    > fact, that is almost exactly its implementation in envir.c) until it
    > hits a top level. However, `emptyenv()` has no enclosing environments
    > so it should be its own top-level environment (I thought).
    > Unfortunately the documentation on environments is relatively sparse,
    > and the R Internals document doesn’t mention top-level environments.

    > Concretely, I encountered this in the following code, which signals an
    > error if `env` is the empty environment:

    > while (! some_complex_condition(env) && ! identical(env, toplevel(env))) {
    >    env = parent.env(env)
    > }

I guess the above 'toplevel(env)' should be replaced by 'topenv(env)' ?

    > Of course there’s a trivial workaround (add an identity check for
    > `emptyenv()` in the while loop condition) but it got me wondering if
    > there’s a rationale for this result or if it’s “accidental”/arbitrary:
    > the C `topenv` implementation defaults to returning R_GlobalEnv for an
    > empty environment. Is this effect actually useful (and used anywhere)?

I don't know if it's useful or used anywhere.
I've not seen topenv() used a lot at all ...  contrary to parent.env(),
and as we know,

  > parent.env(emptyenv())
  Error in parent.env(emptyenv()) : the empty environment has no parent

very much on purpose.

Note that you should not directly be surprised.  If you were you
did not read   ?topenv  carefully enough:

It says that the value returned must be "top level environment" (=: TLE)
and then defines TLE as

>  An environment is considered top level if it is the internal environment of a
>  namespace, a package environment in the search path, or .GlobalEnv .

So from that definition it must return .Globalenv in this
particular case.

On the other hand, if you want a test of

   identical(env, topenv(env))

to make sense I understand that you'd want to allow  topenv() to
return emptyenv() in your case, i.e., you'd want the above
identical(..) to be true in this case...

Still, with the definition of  TLE  as it has been on the help
page forever,  emptyenv() really does not belong

    > This is in R 3.4.4 but I can’t find an indication that this behaviour
    > was ever changed.

Indeed... and as I mentioned I had never actively noticed the
use of topenv() at all...

Martin


    > Cheers

    > --
    > Konrad Rudolph

    > ______________________________________________
    > [hidden email] mailing list
    > https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: topenv of emptyenv

Gábor Csárdi
On Thu, Mar 28, 2019 at 11:43 AM Martin Maechler
<[hidden email]> wrote:
[...]
>
> Indeed... and as I mentioned I had never actively noticed the
> use of topenv() at all...

FWIW topenv() is used in a couple of packages, although some of these
are false positives:
https://github.com/search?q=org%3Acran+topenv&type=Code

Gabor

[...]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: topenv of emptyenv

Konrad Rudolph-2
In reply to this post by Martin Maechler
On Thu, Mar 28, 2019 at 11:42 AM Martin Maechler
<[hidden email]> wrote:
> So from that definition it must return .Globalenv in this
> particular case.

Indeed, that makes sense. Apparently the note wasn’t explicit enough
for me to make the connection.

--
Konrad Rudolph

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: topenv of emptyenv

R devel mailing list
In reply to this post by Gábor Csárdi
And it is used profusely by the methods package.

On Thu, Mar 28, 2019 at 4:53 AM Gábor Csárdi <[hidden email]> wrote:

> On Thu, Mar 28, 2019 at 11:43 AM Martin Maechler
> <[hidden email]> wrote:
> [...]
> >
> > Indeed... and as I mentioned I had never actively noticed the
> > use of topenv() at all...
>
> FWIW topenv() is used in a couple of packages, although some of these
> are false positives:
> https://github.com/search?q=org%3Acran+topenv&type=Code
>
> Gabor
>
> [...]
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>
>

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: topenv of emptyenv

Tomas Kalibera
In reply to this post by Konrad Rudolph-2
On 3/23/19 3:26 PM, Konrad Rudolph wrote:
> I was surprised just now to find out that `topenv(emptyenv())` equals
> … `.GlobalEnv`, not `emptyenv()`. From my understanding of the
> description of `topenv`, it should walk up the chain of enclosing
> environments (as if by calling `e = parent.env(e)` repeatedly; in
> fact, that is almost exactly its implementation in envir.c) until it
> hits a top level. However, `emptyenv()` has no enclosing environments
> so it should be its own top-level environment (I thought).
> Unfortunately the documentation on environments is relatively sparse,
> and the R Internals document doesn’t mention top-level environments.

The intuition here is that topenv() should be used with a reasonable
execution environment and should return a reasonable execution
environment (e.g. things you get from parent.frame(), etc). globalenv()
is a reasonable default execution environment, emtyenv() is not). If you
are using topenv() and especially if you are using it for something that
is not an execution environment, there is a risk you are not doing the
right thing - and this list may be a good place to ask for other
solutions. topenv() as Martin said is not useful in normal user code. It
is used in object systems (S3 and S4), in the byte-code compiler,
sometimes in unit testing frameworks, etc.

As Martin already explained, reading the documentation as that
"topenv(emptyenv())" would be documented to return "emptyenv()" is
logically incorrect.

I agree, however, that it is not completely clear from the documentation
what topenv() would do when given emptyenv() as either argument and we
are working on addressing it. I've been looking into how much of the
CRAN+BIOC tests (all packages) run into the special cases of topenv(),
which btw required many hours of CPU time and some for analysis. The
cases when topenv() is used with emptyenv() are very rare, but they
exist (emptyenv() used as execution environment and S3 dispatch happens,
dummy environment for S4 methods, etc).

Best
Tomas

>
> Concretely, I encountered this in the following code, which signals an
> error if `env` is the empty environment:
>
> while (! some_complex_condition(env) && ! identical(env, toplevel(env))) {
>      env = parent.env(env)
> }
>
> Of course there’s a trivial workaround (add an identity check for
> `emptyenv()` in the while loop condition) but it got me wondering if
> there’s a rationale for this result or if it’s “accidental”/arbitrary:
> the C `topenv` implementation defaults to returning R_GlobalEnv for an
> empty environment. Is this effect actually useful (and used anywhere)?
>
> This is in R 3.4.4 but I can’t find an indication that this behaviour
> was ever changed.
>
> Cheers
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel