From jmc at research.bell-labs.com  Tue Jun 29 17:06:38 2004
From: jmc at research.bell-labs.com (John Chambers)
Date: Tue Jun 29 17:07:06 2004
Subject: [Rd] Problem with hasArg and the ... argument (PR#7027)
References: <20040628091228.B04B8EAD6@slim.kubism.ku.dk>
	<x2d63jdhne.fsf@biostat.ku.dk>
Message-ID: <40E1857E.A7B7E908@research.bell-labs.com>

Peter Dalgaard wrote:
> 
>
<..snip>

> Checking... Yep, the logic in R_sysfunction() is to give the function
> of frame #n:
> 
>     if (n > 0)
>         n = framedepth(cptr) - n;
>     else
>         n = - n;
>     if (n < 0 )
>         errorcall(R_GlobalContext->call, "illegal frame number");
>     while (cptr->nextcontext != NULL) {
>         if (cptr->callflag & CTXT_FUNCTION ) {
>             if (n == 0)
>                 return duplicate(cptr->callfun);  /***** do we need to DUP? */
>             else
>                 n--;
>         }
>         cptr = cptr->nextcontext;
>     }
> 
> whereas the documentation has
> 
>      'sys.function' gives the definition of the function currently
>      being evaluated in the frame 'n' generations back.
> 
> However, we're using the current convention in quite a few places
> (sys.function(sys.parent())) and, worse, who know what packages might
> do. It is tempting just to change the documentation, but it is part of
> a grouped documentation of sys.whatever, which has
> 
>    which: the frame number if non-negative, the number of generations
>           to go back if negative. (See the Details section.)
> 
>        n: the number of frame generations to go back.
> 
> and sys.function is documented with argument 'n', which we'd have to
> change to 'which', but the default is n=0 for "current function" which
> is unlike 'which' which has 0 meaning .GlobalEnv. Argh...
> 
> My take is that we need to fix sys.function to behave according to
> docs, change what we can in the R internals, and face the consequences
> for package maintainers.

Things are actually messier, even.

1. A counter-argument for changing the documentation might be that the
green book (p 106) and S-Plus take the argument to be the frame number
(only sys.parent(n) uses the argument for the number of frames back).

Unfortunately for the counter-argument, the (current) R implementation
and the S-Plus implementation differ in where they start indexing.  In
S-Plus, 1 is the top-level frame (corresponding to the global
environment).  In R, it is the first function call frame (and 0
corresponds to the global environment).

So there seems no way to have R/S-Plus compatibility.

2. And R-only consistency does not look too good either: sys.call() and
sys.frame() claim to have the which= behavior.  It's not very natural
for sys.function() to behave differently.

And even if that didn't bother us, sys.call(0) returns the current call,
not the "global call" (whatever that would mean), regardless of
documentation.  (The test at the bottom of this mail illustrates.)

What to do?  Well, a tentative suggestion.  
- Leave the implementation almost alone--no simple fix will clean up all
the problems. Optionally make one change: If sys.frame(0) produced the
frame of the current call, then sys.function, sys.call, and sys.frame
would be consistent.

- Change the documentation to give sys.function argument `which',
explaining that which=0 is interpreted as the current
call/function/frame.

If we wanted to leave the implementation totally unchanged, then we have
to admit the inconsistency in sys.frame, and tell people to use
sys.frame(sys.nframe()) to produce the current frame.

John



> 
> --
>    O__  ---- Peter Dalgaard             Blegdamsvej 3
>   c/ /'_ --- Dept. of Biostatistics     2200 Cph. N
>  (*) \(*) -- University of Copenhagen   Denmark      Ph: (+45) 35327918
> ~~~~~~~~~~ - (p.dalgaard@biostat.ku.dk)             FAX: (+45) 35327907
> 
> ______________________________________________
> R-devel@stat.math.ethz.ch mailing list
> https://www.stat.math.ethz.ch/mailman/listinfo/r-devel

-- 
John M. Chambers                  jmc@bell-labs.com
Bell Labs, Lucent Technologies    office: (908)582-2681
700 Mountain Avenue, Room 2C-282  fax:    (908)582-3340
Murray Hill, NJ  07974            web: http://www.cs.bell-labs.com/~jmc

--------------------------------------------------------------------
Example.  Source in the following function definitions:

foo <- function(n)bar(n)
bar <- function(n)baz(n)
baz <- function(n)list(sys.function(n), sys.call(n), sys.frame(n))

Call foo() with various arguments.  In R, you get:

R> foo(1)
[[1]]
function(n)bar(n)

[[2]]
foo(1)

[[3]]
<environment: 0x8eb6378>

R> foo(2)
[[1]]
function(n)baz(n)

[[2]]
bar(n)

[[3]]
<environment: 0x8eb6d24>

R> foo(3)
[[1]]
function(n)list(sys.function(n), sys.call(n), sys.frame(n))

[[2]]
baz(n)

[[3]]
<environment: 0x8eb7e68>

R> foo(0)
[[1]]
function(n)list(sys.function(n), sys.call(n), sys.frame(n))

[[2]]
baz(n)

[[3]]
<environment: R_GlobalEnv>


-----------------------------------

In S-Plus you get:

S+> foo(1)
[[1]]:
NULL

[[2]]:
[[2]]$.Last.expr:
expression(foo(1))

[[2]]$.Auto.print:
[1] T


S+> foo(2)
[[1]]:
function(n)
bar(n)

[[2]]:
foo(2)

[[3]]:
[[3]]$n:
[1] 2


S+> foo(3)
[[1]]:
function(n)
baz(n)

[[2]]:
bar(n)

[[3]]:
[[3]]$n:
[1] 3


S+> foo(0)
Problem in sys.function(n): Illegal frame number (0), should be between
1 and 5
Debug ?  ( y|n ): n


[Rd] Problem with hasArg and the ... argument (PR#7027)
John Chambers