[Rd] Problem with S4 inheritance: unexpected re-initialization?

cstrato cstrato at aon.at
Fri Apr 6 19:10:13 CEST 2007


Dear Herve, dear Martin,

First I want to thank Herve very much for your continuous efforts. You can
not imagine, how relieved I was that you mangaged to reproduce this 
behavior.

Just when I wanted to send my reply to Herve, I received Martin's comments:
As you mention, there were two problems in my example:

1. partial matching:
Thank you for your explanation, now I hope that I understand the behavior.
However, as Herve correctly mentioned, this is only a side issue.

2. lazy evaluation and validity methods:
This is the real problem, that I try to solve since three weeks, and I am
afraid, that I still do not know how to solve it.

Following Herve's example, I have managed to reduce my own example so that
it is only slightly larger than his example, however, now I had to expand
it again to make my point, see the final code below.
(For the moment I stick with my naming of the classes)

The result I get is the following:
 > subsubB2 <- new("SubSubClassB2", filename="MyFileNameB2", 
nameB="MyNameB")
------initialize:SubSubClassB2------
------initialize:SubClassB------
------initialize:BaseClass------
BaseClass:init:filename = MyFileNameB2
------setValidity:BaseClass------
------initialize:SubClassB------
------initialize:BaseClass------
BaseClass:init:filename = ERROR_FileName
------setValidity:BaseClass------
------setValidity:SubClassB------
------setValidity:SubClassB------
------setValidity:SubSubClassB2------
SubSubClassB2:val:filename = MyFileNameB2

Although the final output gives the correct result "filename = 
MyFileNameB2",
in the interim code, filename is set to "filename = ERROR_FileName".

In my real package the line "filename <- ERROR_FileName" is replaced by
a function. To take advantage of inheritance, I have defined "filename"
in BaseClass, and I want to check for its validity only once in BaseClass.

With the current information I have to re-evaluate my code in order to
avoid "lazy evaluation", since this might be the problem.

Thank you both for your extensive help.
Best regards
Christian

# - - - - - - - - - - - - - - - BEGIN - - - - - - - - - - - - - - - -
setClass("BaseClass",
   representation(filename = "character", "VIRTUAL"),
   prototype(filename = "DefaultFileName")
)

setClass("SubClassB",
   representation(nameB = "character"),
   contains=c("BaseClass"),
   prototype(nameB = "NameB")
)

setClass("SubSubClassB2",
   representation(nameB2 = "character"),
   contains=c("SubClassB"),
   prototype(nameB2 = "NameB2")
)

setMethod("initialize", "BaseClass",
   function(.Object, filename="",  ...) {
      cat("------initialize:BaseClass------\n")
      if (filename == "" || nchar(filename) == 0) filename <- 
"ERROR_FileName"
      cat("BaseClass:init:filename = ", filename, "\n", sep="")
      .Object <- callNextMethod(.Object, filename=filename, ...)
      .Object at filename <- filename
      .Object
   }
)

setValidity("BaseClass",
   function(object) {
      cat("------setValidity:BaseClass------\n")
      msg <- NULL
      str <- object at filename
      if (!(is.character(object at filename)))
         msg <- cat("missing filename\n")
      if (is.null(msg)) TRUE else msg
   }
)

setMethod("initialize", "SubClassB",
   function(.Object, nameB="",  ...) {
      cat("------initialize:SubClassB------\n")
      .Object <- callNextMethod(.Object, nameB=nameB, ...)
   }
)

setValidity("SubClassB",
   function(object) {
      cat("------setValidity:SubClassB------\n")
      TRUE
   }
)

setMethod("initialize", "SubSubClassB2",
   function(.Object, ...) {
      cat("------initialize:SubSubClassB2------\n")
      .Object <- callNextMethod(.Object, ...)
   }
)

setValidity("SubSubClassB2",
   function(object) {
      cat("------setValidity:SubSubClassB2------\n")
      cat("SubSubClassB2:val:filename = ", object at filename, "\n", sep="")
      TRUE
   }
)
# - - - - - - - - - - - - - - - END - - - - - - - - - - - - - - - - -


Martin Morgan wrote:
> Christian, Herve --
>
> In the end, I think you're being caught by partial matching in
> function arguments:
>
>   
>> f <- function(x1) cat("x1:", x1, "\n")
>> f(x="hello")
>>     
> x1: hello 
>
> In the functional call x gets matched to x1. In your initialization
> method for SubSubClassB2, nameB gets matched to nameB2 and so not
> passed as ... in the callNextMethod.
>
> The apparently extra calls to initialize with the wrong arguments come
> about because of the validity methods -- validity gets checked by
> coercing derived objects to their superclass, which involves a call to
> 'new' (hence initialize) followed by copying appropriate slots.
>
> The funny effect where class(object) seems to trigger construction of
> a new object is lazy evaluation -- the 'object' argument to
> setValidity is not evaluated until needed, i.e., until class(object)
> (anything would trigger this, including force(object)); only then do
> you see the attempt to create the new object of the previous
> paragraph.
>
> Without partial matching issues, the use of callNextMethod as here:
>
> setMethod("initialize", "SubSubClassB2",
>    function(.Object, nameB2="MyNameB2", ...) {
>       if (nameB2 == "") nameB2 <- "DefaultNameB2";
>       callNextMethod(.Object, nameB2=nameB2, ...)
>    }
> )
>
> is fine (though weird to have a prototype, a named argument, and a
> conditional assignment!).
>
> With
>
>   setMethod("initialize", "SubSubClassB2",
>     function(.Object, ...) {
>        .Object <- callNextMethod()
>        if (.Object at nameB2 == "") .Object at nameB2 <- "DefaultNameB2"
>        .Object
>     }
>   )
>
> as suggested by Herve, the "SubClassB" initializer matches the nameB
> argument to 'new' to the nameB argument of the initialize method for
> SubClassB. 
>
> Simplifying the code to illustrate the problem was very helpful. It
> would have been better to not present the validity methods (this might
> have been a different thread), to remove the cat statements, to remove
> unnecessary slots and classes (SubSubClassB1) and to name classes,
> slots, and argument values in an easier to remember way (e.g., classes
> A, B, C, slots a, b, c)
>
> Hope that helps. 
>
>
> Martin
>
>
>



More information about the R-devel mailing list