Winston Chang
2013-Oct-16 19:47 UTC
[Rd] Internally accessing ref class methods with .self$x is different from .self[['x']]
When a reference class method is accessed with .self$x, it has different behavior from .self[['x']]. The former copies the function to the object's environment (with some attributes attached), and the latter just return NULL (unless it has already been accessed once with .self$x). Is this how it's supposed to work? Here's an example that illustrates: https://gist.github.com/wch/7013262 TestClass <- setRefClass( 'TestClass', fields = list(a = 'ANY'), methods = list( initialize = function() { e <- environment() pe <- parent.env(e) ppe <- parent.env(pe) # The environment of this object print(ls(pe)) # The environment of the class print(ls(ppe)) # No surprises with fields cat("==================== .self[['a']] ====================\n") print(.self[['a']]) # Getting b these ways isn't quite what we want cat("==================== .self[['b']] ====================\n") print(.self[['b']]) cat("==================== b ====================\n") print(b) # Accessing b with $ works, and it changes things from here on in cat("==================== .self$b ====================\n") print(.self$b) # Now these return the b method with some attributes attached cat("==================== .self[['b']] ====================\n") print(.self[['b']]) cat("==================== b ====================\n") print(b) cat("===============================================\n") print(ls(parent.env(e))) print(ls(parent.env(parent.env(e)))) }, b = function() { "Yes, this is b." } ) ) tc <- TestClass$new() Output: =========================================================== [1] "a" "initialize" [1] "b" "e" "tc" "TestClass" ==================== .self[['a']] ===================An object of class "uninitializedField" Slot "field": [1] "a" Slot "className": [1] "ANY" ==================== .self[['b']] ===================NULL ==================== b ===================function() { "Yes, this is b." } ==================== .self$b ===================function() { "Yes, this is b." } <environment: 0x5a65418> attr(,"mayCall") character(0) attr(,"name") [1] "b" attr(,"refClassName") [1] "TestClass" attr(,"superClassMethod") [1] "" attr(,"class") [1] "refMethodDef" attr(,"class")attr(,"package") [1] "methods" ==================== .self[['b']] ===================function() { "Yes, this is b." } <environment: 0x5a65418> attr(,"mayCall") character(0) attr(,"name") [1] "b" attr(,"refClassName") [1] "TestClass" attr(,"superClassMethod") [1] "" attr(,"class") [1] "refMethodDef" attr(,"class")attr(,"package") [1] "methods" ==================== b ===================function() { "Yes, this is b." } <environment: 0x5a65418> attr(,"mayCall") character(0) attr(,"name") [1] "b" attr(,"refClassName") [1] "TestClass" attr(,"superClassMethod") [1] "" attr(,"class") [1] "refMethodDef" attr(,"class")attr(,"package") [1] "methods" ==============================================[1] "a" "b" "initialize" [1] "b" "e" "tc" "TestClass" -Winston
Gabriel Becker
2013-Oct-16 20:41 UTC
[Rd] Internally accessing ref class methods with .self$x is different from .self[['x']]
Winston, (back on list since I found some official info) Looks like the behavior you are seeing is "documented-ish" Only methods actually used will be included in the environment corresponding to an individual object. To declare that a method requires a particular other method, the first method should include a call to $usingMethods() with the name of the other method as an argument. Declaring the methods this way is essential if the other method is used indirectly (e.g., via sapply<http://stat.ethz.ch/R-manual/R-devel/library/base/html/lapply.html> () or do.call<http://stat.ethz.ch/R-manual/R-devel/library/base/html/do.call.html> ()). If it is called directly, code analysis will find it. Declaring the method is harmless in any case, however, and may aid readability of the source code. Seems like .self$usingMethods() is supposed to allow you to do what you want, but I wasn't able to get it to work after a few minutes of fiddling and the actual usingMethods method doesn't seem to do anything on cursory inspection in a toy example but I don't pretend to know the arcane depths of the refclass machinery. ~G On Wed, Oct 16, 2013 at 1:14 PM, Winston Chang <winstonchang1@gmail.com>wrote:> Hi Gabriel - > > Thanks for your reply. The reason that I'm interested in doing it that > way is because we're working on a project where we call a method and > pass it the name of another method, and that method is accessed with $ > or [[. > > You might be right about [[ treating it as a standard environment. > Reading the docs a little more closely, I found: > "... The corresponding programming mechanism is to invoke a method on > an object. In the R syntax we use "$" for this operation; one invokes > a method, m1 say, on an object x by the expression x$m1(...)." > > This works as expected, but it is a bit clunky: > `$`(.self, x) > > -Winston > > > On Wed, Oct 16, 2013 at 3:08 PM, Gabriel Becker <gmbecker@ucdavis.edu> > wrote: > > Winston, > > > > Replying off list as I'm not an expert and don't have time to delve into > > this enough to actually track it down. > > > > Is there a reason to expect [[ to work in this way? I've only ever > > seen/written code that access fields/methods on reference classes via $ > > (which I assume has extra machinery for RefClasses to deal with active > > binding functions for fields, etc). > > > > My guess would be that [[ is treating the reference class obj as a > standard > > environment which is bypassing some extra step necessary for refclass > > objects and that is causing the problems. > > > > > > I'd say its probably still a bug, but of the "why were you trying to do > > that?" variety (no offense intended to you at all). > > > > All I can really suggest in the interim is to just use $ instead until > John > > (Chambers) pops up and responds to your mail. > > > > ~G > > > > > > On Wed, Oct 16, 2013 at 12:47 PM, Winston Chang <winstonchang1@gmail.com > > > > wrote: > >> > >> When a reference class method is accessed with .self$x, it has > >> different behavior from .self[['x']]. The former copies the function > >> to the object's environment (with some attributes attached), and the > >> latter just return NULL (unless it has already been accessed once with > >> .self$x). Is this how it's supposed to work? > >> > >> Here's an example that illustrates: https://gist.github.com/wch/7013262 > >> > >> TestClass <- setRefClass( > >> 'TestClass', > >> fields = list(a = 'ANY'), > >> methods = list( > >> initialize = function() { > >> e <- environment() > >> pe <- parent.env(e) > >> ppe <- parent.env(pe) > >> > >> # The environment of this object > >> print(ls(pe)) > >> # The environment of the class > >> print(ls(ppe)) > >> > >> # No surprises with fields > >> cat("==================== .self[['a']] ====================\n") > >> print(.self[['a']]) > >> > >> > >> # Getting b these ways isn't quite what we want > >> cat("==================== .self[['b']] ====================\n") > >> print(.self[['b']]) > >> cat("==================== b ====================\n") > >> print(b) > >> > >> > >> # Accessing b with $ works, and it changes things from here on in > >> cat("==================== .self$b ====================\n") > >> print(.self$b) > >> > >> > >> # Now these return the b method with some attributes attached > >> cat("==================== .self[['b']] ====================\n") > >> print(.self[['b']]) > >> cat("==================== b ====================\n") > >> print(b) > >> > >> > >> cat("===============================================\n") > >> print(ls(parent.env(e))) > >> print(ls(parent.env(parent.env(e)))) > >> > >> }, > >> > >> b = function() { > >> "Yes, this is b." > >> } > >> ) > >> ) > >> > >> tc <- TestClass$new() > >> > >> > >> Output: > >> ===========================================================> >> > >> [1] "a" "initialize" > >> [1] "b" "e" "tc" "TestClass" > >> ==================== .self[['a']] ===================> >> An object of class "uninitializedField" > >> Slot "field": > >> [1] "a" > >> > >> Slot "className": > >> [1] "ANY" > >> > >> ==================== .self[['b']] ===================> >> NULL > >> ==================== b ===================> >> function() { > >> "Yes, this is b." > >> } > >> ==================== .self$b ===================> >> function() { > >> "Yes, this is b." > >> } > >> <environment: 0x5a65418> > >> attr(,"mayCall") > >> character(0) > >> attr(,"name") > >> [1] "b" > >> attr(,"refClassName") > >> [1] "TestClass" > >> attr(,"superClassMethod") > >> [1] "" > >> attr(,"class") > >> [1] "refMethodDef" > >> attr(,"class")attr(,"package") > >> [1] "methods" > >> ==================== .self[['b']] ===================> >> function() { > >> "Yes, this is b." > >> } > >> <environment: 0x5a65418> > >> attr(,"mayCall") > >> character(0) > >> attr(,"name") > >> [1] "b" > >> attr(,"refClassName") > >> [1] "TestClass" > >> attr(,"superClassMethod") > >> [1] "" > >> attr(,"class") > >> [1] "refMethodDef" > >> attr(,"class")attr(,"package") > >> [1] "methods" > >> ==================== b ===================> >> function() { > >> "Yes, this is b." > >> } > >> <environment: 0x5a65418> > >> attr(,"mayCall") > >> character(0) > >> attr(,"name") > >> [1] "b" > >> attr(,"refClassName") > >> [1] "TestClass" > >> attr(,"superClassMethod") > >> [1] "" > >> attr(,"class") > >> [1] "refMethodDef" > >> attr(,"class")attr(,"package") > >> [1] "methods" > >> ==============================================> >> [1] "a" "b" "initialize" > >> [1] "b" "e" "tc" "TestClass" > >> > >> > >> > >> -Winston > >> > >> ______________________________________________ > >> R-devel@r-project.org mailing list > >> https://stat.ethz.ch/mailman/listinfo/r-devel > > > > > > > > > > -- > > Gabriel Becker > > Graduate Student > > Statistics Department > > University of California, Davis >-- Gabriel Becker Graduate Student Statistics Department University of California, Davis [[alternative HTML version deleted]]