Mark.Bravington@csiro.au
2002-Sep-04 03:19 UTC
[R] strange things with eval and parent frames
Dear mailing list, I have found some strange behaviour which I think relates to parent frames and eval. Can anyone explain what's going on here? First example:> test.parent.funcs_ function() {outer.var_ 5 subfunc1_ function() substitute( outer.var, envir=parent.frame()) print( subfunc1()) subfunc2b_ function() eval( quote( outer.var), envir=parent.frame()) print( subfunc2b()) subfunc2_ function() evalq( outer.var, envir=parent.frame()) #print( subfunc2()) }> test.parent.funcs()[1] 5 [1] 5 If you try this without the hash before the 3rd print statement, it crashes, complaining it can't find outer.var. But AFAICS, subfunc2 and subfunc2b should return the same thing. So, at a minimum, evalq(...) doesn't seem to be behaving the same as eval( quote(...)) as the documentation says it should. But I think more fundamentally odd things are happening, as per below. Note BTW that this is NOT a classical scoping problem. Exactly the same things happen if I define subfunc1 etc. outside the body of test.parent.funcs. Second example:> test.parent.funcs2_ function() {subfunc3_ function() sys.call( -1) x_ subfunc3(); print( x) subfunc3a_ function() eval( quote( sys.call()), parent.frame()) x_ subfunc3a(); print( x) subfunc3b_ function() evalq( sys.call(), parent.frame()) x_ subfunc3b(); print( x) }> test.parent.funcs2()test.parent.funcs2() eval(expr, envir, enclos) eval(expr, envir, enclos) Hmmm. Again, I'd expect all three to do the same thing (have I misunderstood?). What's even more odd, is that this time evalq and eval( quote()) do coincide, but neither of them gives the answer I expected. I thought that by doing the evaulation in the parent.frame, I could knock 1 level off the number of generations to go back in sys.call. Why would anyone care? Well, the proximate reason for bothering to post this is that I got very confused while using a debugger, which "manually" evaluates expressions within the original frame (i.e. by calling eval). When the functions I was debugging made calls to "parent.frame", everything went very pear-shaped. However, I have a feeling that I've also hit this before in another context. In the above cases, there happens to be a workaround, but it's somewhat due to luck. Some light can be shed on this by including some calls to sys.parents in the sub-functions, but it's not easy to summarize the results. My best shot at a short, comprehensible description is as follows. An environment can appear several times in the list of active frames, albeit with different frame numbers and different "parent frame numbers" each time. The environment is IDENTICAL in each case-- you can get this from lapply( sys.frames(), print)-- but it has a different "alias" (i.e. frame number each time). Calling "sys.parent" seems to return the nominal parent of the LATEST incarnation in the frame list. My suggestion (a hunch) is that everything would actually work fine if, instead, sys.parent checked for the FIRST incarnation of the current frame, and took the parent of that-- at least as a default option. Incidentally, is there any way of testing whether two environments are identical (e.g. between elements of the list returned by sys.frames())? The operator == doesn't work, and nor does "all.equal". The only fudge I could devise, was to print them via a textConnection, and compare the text representations... ugh. Can anyone shed any light on this? cheers Mark ******************************* Mark Bravington CSIRO (CMIS) PO Box 1538 Castray Esplanade Hobart TAS 7001 phone (61) 3 6232 5118 fax (61) 3 6232 5012 Mark.Bravington at csiro.au -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- r-help mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html Send "info", "help", or "[un]subscribe" (in the "body", not the subject !) To: r-help-request at stat.math.ethz.ch _._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
Mark.Bravington at csiro.au writes:> Dear mailing list, > > I have found some strange behaviour which I think relates to parent frames > and eval. Can anyone explain what's going on here? > > First example: > > > test.parent.funcs_ function() { > outer.var_ 5 > subfunc1_ function() substitute( outer.var, envir=parent.frame()) > print( subfunc1()) > > subfunc2b_ function() eval( quote( outer.var), envir=parent.frame()) > print( subfunc2b()) > > subfunc2_ function() evalq( outer.var, envir=parent.frame()) > #print( subfunc2()) > } > > > test.parent.funcs() > [1] 5 > [1] 5 > > If you try this without the hash before the 3rd print statement, it crashes, > complaining it can't find outer.var. But AFAICS, subfunc2 and subfunc2b > should return the same thing. So, at a minimum, evalq(...) doesn't seem to > be behaving the same as eval( quote(...)) as the documentation says it > should. But I think more fundamentally odd things are happening, as per > below. > > Note BTW that this is NOT a classical scoping problem. Exactly the same > things happen if I define subfunc1 etc. outside the body of > test.parent.funcs.This is a bug, but not an easy one to fix: Internally, evalq does eval.parent(substitute(eval(quote(expr), envir, enclos))) which works for any envir except if the expression for it involves parent.frame. The generic problem is that normally eval.parent(substitute(x)) is equivalent to x but if there's an examination of the call stack involved (using sys.call() or parent.frame() etc.) they are not... You can work around it with function() {p<-parent.frame();evalq( outer.var, envir=p) }> Second example: > > test.parent.funcs2_ function() { > subfunc3_ function() sys.call( -1) > x_ subfunc3(); print( x) > > subfunc3a_ function() eval( quote( sys.call()), parent.frame()) > x_ subfunc3a(); print( x) > > subfunc3b_ function() evalq( sys.call(), parent.frame()) > x_ subfunc3b(); print( x) > } > > > test.parent.funcs2() > test.parent.funcs2() > eval(expr, envir, enclos) > eval(expr, envir, enclos) > > Hmmm. Again, I'd expect all three to do the same thing (have I > misunderstood?). What's even more odd, is that this time evalq and eval( > quote()) do coincide, but neither of them gives the answer I expected. I > thought that by doing the evaulation in the parent.frame, I could knock 1 > level off the number of generations to go back in sys.call.This one is not a bug. There's just no way that you can make the system pretend that the call to eval() or evalq() didn't happen. (And sys.call() is independent of evaluation environment, so the parent.frame() anomaly doesn't matter). -- O__ ---- Peter Dalgaard Blegdamsvej 3 c/ /'_ --- Dept. of Biostatistics 2200 Cph. N (*) \(*) -- University of Copenhagen Denmark Ph: (+45) 35327918 ~~~~~~~~~~ - (p.dalgaard at biostat.ku.dk) FAX: (+45) 35327907 -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- r-help mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html Send "info", "help", or "[un]subscribe" (in the "body", not the subject !) To: r-help-request at stat.math.ethz.ch _._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
>>>>> "Mark" == Mark Bravington <Mark.Bravington at csiro.au> >>>>> on Wed, 4 Sep 2002 13:19:47 +1000 writes:<.... ((much more)) ....> Mark> Incidentally, is there any way of testing whether two Mark> environments are identical (e.g. between elements of Mark> the list returned by sys.frames())? The operator = Mark> doesn't work, and nor does "all.equal". The only fudge Mark> I could devise, was to print them via a Mark> textConnection, and compare the text Mark> representations... ugh. "ooaeeh", indeed! The proper solution is identical(.,.) which has been new only since 1.4.0, (a John Chambers' feature of S4, hence also in newer versions of S-plus,) e.g., > identical(.GlobalEnv, environment()) [1] TRUE identical() has still been underused quite a bit, probably even in base R code. In many cases, identical(x,y) is now preferable to all(x == y) even when the latter does work. Martin -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- r-help mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html Send "info", "help", or "[un]subscribe" (in the "body", not the subject !) To: r-help-request at stat.math.ethz.ch _._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
Mark.Bravington@csiro.au
2002-Sep-04 09:35 UTC
[R] strange things with eval and parent frames
Thanks for the very quick and helpful explanation, Peter (and to Martin Maechler for telling me about identical() ). I'm still puzzled by the sys.call case. If I fake the issue by going one level deeper, and then calling sys.call( sys.parent()) instead of sys.call(), I do get what I expected to get (i.e. the call to the top-level function). The example is: test.parent.funcs3_ function() { subfunc3_ function() sys.call( -1) x_ subfunc3(); print( x) # the top-level call subfunc3a_ function() eval( quote( sys.call()), parent.frame()) x_ subfunc3a(); print( x) # I wanted the top level call, but the "eval" gets intercepted subfunc3c_ function() eval( quote( { function() sys.call( sys.parent()) }() ), parent.frame()) x_ subfunc3c(); print( x) # the top level call }> test.parent.funcs3()test.parent.funcs3() eval(expr, envir, enclos) test.parent.funcs3() The 3rd output line, which creates a temporary function and calls it, "works" or at any rate is different from subfunc3a. The behaviour of sys.call still seems inconsistent to me: if there's no way to pretend that eval didn't happen, as you say, then isn't it a bug that you can fool the system by going down a level and then up again? Actually I'd rather not have the "fool" "bug" fixed, because it does give me a workround :), as follows: my.sys.call_ function(which=0) { if( which==0) sys.call( sys.parent()) else if( which<0) sys.call( which-1) else sys.call( which) } Note also: test.parent.funcs4()_ function( ...) { subfunc1_ function() eval( quote( match.call()), envir=parent.frame()) x_ subfunc1(); print( x) }> test.parent.funcs4()test.parent.funcs4() so that match.call works differently from sys.call here (it "ignores" the call to eval). cheers Mark -----Original Message----- From: Peter Dalgaard BSA To: Mark.Bravington at csiro.au Cc: r-help at stat.math.ethz.ch Sent: 4/09/2002 4:14 PM Subject: Re: [R] strange things with eval and parent frames Mark.Bravington at csiro.au writes:> Dear mailing list, > > I have found some strange behaviour which I think relates to parentframes> and eval. Can anyone explain what's going on here? ><< deleted stuff >>> Second example: > > test.parent.funcs2_ function() { > subfunc3_ function() sys.call( -1) > x_ subfunc3(); print( x) > > subfunc3a_ function() eval( quote( sys.call()), parent.frame()) > x_ subfunc3a(); print( x) > > subfunc3b_ function() evalq( sys.call(), parent.frame()) > x_ subfunc3b(); print( x) > } > > > test.parent.funcs2() > test.parent.funcs2() > eval(expr, envir, enclos) > eval(expr, envir, enclos) > > Hmmm. Again, I'd expect all three to do the same thing (have I > misunderstood?). What's even more odd, is that this time evalq andeval(> quote()) do coincide, but neither of them gives the answer I expected.I> thought that by doing the evaulation in the parent.frame, I couldknock 1> level off the number of generations to go back in sys.call.This one is not a bug. There's just no way that you can make the system pretend that the call to eval() or evalq() didn't happen. (And sys.call() is independent of evaluation environment, so the parent.frame() anomaly doesn't matter). -- O__ ---- Peter Dalgaard Blegdamsvej 3 c/ /'_ --- Dept. of Biostatistics 2200 Cph. N (*) \(*) -- University of Copenhagen Denmark Ph: (+45) 35327918 ~~~~~~~~~~ - (p.dalgaard at biostat.ku.dk) FAX: (+45) 35327907 -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.- r-help mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html Send "info", "help", or "[un]subscribe" (in the "body", not the subject !) To: r-help-request at stat.math.ethz.ch _._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._