Hi, If needed, lapply() tries to convert its first argument into a list before it starts doing something with it: > lapply function (X, FUN, ...) { FUN <- match.fun(FUN) if (!is.vector(X) || is.object(X)) X <- as.list(X) .Internal(lapply(X, FUN)) } But in practice, things don't always seem to "work" as suggested by this code (at least to the eyes of a naive user). I have defined an "as.list" method for my S4 class "A": > setClass("A", representation(data="list")) [1] "A" > setMethod("as.list", "A", function(x, ...) x at data) Creating a new generic function for "as.list" in ".GlobalEnv" [1] "as.list" Testing it: > a <- new("A", data=list(8, 2:0)) > as.list(a) [[1]] [1] 8 [[2]] [1] 2 1 0 OK. But lapply() doesn't work on 'a': > lapply(a, typeof) Error in as.vector(x, "list") : cannot type 'S4' coerce to vector I still have to do the 'as.list(a)' part myself for things to work: > lapply(as.list(a), typeof) [[1]] [1] "double" [[2]] [1] "integer" Seems like using force() inside lapply() would solve the problem: lapply2 <- function(X, FUN, ...) { FUN <- match.fun(FUN) if (!is.vector(X) || is.object(X)) X <- force(as.list(X)) .Internal(lapply(X, FUN)) } It works now: > lapply2(a, typeof) [[1]] [1] "double" [[2]] [1] "integer" Cheers, H.
The problem of promises not being evaluated in lists has been discussed before. I think its fixed in the development version of R. See r44139 on Jan 24, 2008 in http://developer.r-project.org/R.svnlog.2007 On Jan 31, 2008 1:26 PM, <hpages at fhcrc.org> wrote:> Hi, > > If needed, lapply() tries to convert its first argument into a list > before it starts doing something with it: > > > lapply > function (X, FUN, ...) > { > FUN <- match.fun(FUN) > if (!is.vector(X) || is.object(X)) > X <- as.list(X) > .Internal(lapply(X, FUN)) > } > > But in practice, things don't always seem to "work" as suggested by > this code (at least to the eyes of a naive user). > > I have defined an "as.list" method for my S4 class "A": > > > setClass("A", representation(data="list")) > [1] "A" > > setMethod("as.list", "A", function(x, ...) x at data) > Creating a new generic function for "as.list" in ".GlobalEnv" > [1] "as.list" > > Testing it: > > > a <- new("A", data=list(8, 2:0)) > > as.list(a) > [[1]] > [1] 8 > > [[2]] > [1] 2 1 0 > > OK. > > But lapply() doesn't work on 'a': > > > lapply(a, typeof) > Error in as.vector(x, "list") : cannot type 'S4' coerce to vector > > I still have to do the 'as.list(a)' part myself for things to work: > > > lapply(as.list(a), typeof) > [[1]] > [1] "double" > > [[2]] > [1] "integer" > > Seems like using force() inside lapply() would solve the problem: > > lapply2 <- function(X, FUN, ...) > { > FUN <- match.fun(FUN) > if (!is.vector(X) || is.object(X)) > X <- force(as.list(X)) > .Internal(lapply(X, FUN)) > } > > It works now: > > > lapply2(a, typeof) > [[1]] > [1] "double" > > [[2]] > [1] "integer" > > Cheers, > H. > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >
>>>>> "HP" == Herve Pages <hpages at fhcrc.org> >>>>> on Thu, 31 Jan 2008 10:26:31 -0800 writes:HP> Hi, If needed, lapply() tries to convert its first HP> argument into a list before it starts doing something HP> with it: >> lapply HP> function (X, FUN, ...) HP> { HP> FUN <- match.fun(FUN) HP> if (!is.vector(X) || is.object(X)) HP> X <- as.list(X) HP> .Internal(lapply(X, FUN)) HP> } HP> But in practice, things don't always seem to "work" as suggested by HP> this code (at least to the eyes of a naive user). Yes. That is the infamous problem of what I'd call a mental conflict between namespaces and function-centered OOP (as in S3 or S4), or put differently, the problem that not all R functions are S4 generics right from the start : Both lapply() and as.list() are in the base namespace. Consequently, lapply() will always call base::as.list() and unfortunately base::as.list() is not becoming an S4 generic by your >> setClass("A", representation(data="list")) HP> [1] "A" >> setMethod("as.list", "A", function(x, ...) x at data) HP> Creating a new generic function for "as.list" in ".GlobalEnv" HP> [1] "as.list" See: it's an S4 generic only in .GlobalEnv, but to really work it should be an S4 generic in base. HP> Seems like using force() inside lapply() would solve the problem: Well, it's not force() that makes it work, it's the fact that you define a version of lapply outside "base" and that of course does see your as.list() generic in .GlobalEnv .. HP> lapply2 <- function(X, FUN, ...) HP> { HP> FUN <- match.fun(FUN) HP> if (!is.vector(X) || is.object(X)) HP> X <- force(as.list(X)) HP> .Internal(lapply(X, FUN)) HP> } HP> It works now: [...........] Now one "solution" to the problem is to redefine base::as.list() to be your S4 generic. Most smart useRs I know would call this a terrible hack though... and yes, I'm guilty of committing that hack -- inside the Matrix package, not for as.list() but for as.matrix(): The consequence of that is that e.g. eigen() *does* work for all our matrices, because eigen starts with as.matrix() and that needs to work as a proper (i.e. S4) generic in order to work as it should. A much better solution to the underlying deeper problem would be to find a way where ___ conceptually ___ part (or all of) 'methods' would be inside of 'base', and as.list(), as.matrix() etc all S4 (and S3 simultaneously) generics. [and we have pondered of using 'base4' for that, which would contain part of current base and part of current methods; but there have been different ideas]. One workaround/solution used recently (notably for the group generics) was to make more of these functions into *primitive* functions with C-internal S3 and S4 method dispatch. That maybe a desideratum for "now". Martin Maechler, ETH Zurich (and R core team)