Martin Maechler
2019-Nov-15 16:31 UTC
[Rd] class(<matrix>) |--> c("matrix", "arrary") [was "head.matrix ..."]
>>>>> Pages, Herve >>>>> on Thu, 14 Nov 2019 19:13:47 +0000 writes:> On 11/14/19 05:47, Hadley Wickham wrote: >> On Sun, Nov 10, 2019 at 2:37 AM Martin Maechler >> <maechler at stat.math.ethz.ch> wrote: >>> >>>>>>>> Gabriel Becker >>>>>>>> on Sat, 2 Nov 2019 12:37:08 -0700 writes: >>> >>> > I agree that we can be careful and narrow and still see a >>> > nice improvement in behavior. While Herve's point is valid >>> > and I understand his frustration, I think staying within >>> > the matrix vs c(matrix, array) space is the right scope >>> > for this work in terms of fiddling with inheritance. >>> >>> [.................] >>> >>> >>>>> Also, we seem to have a rule that inherits(x, c) iff c %in% class(x), >>>> >>>> good point, and that's why my usage of inherits(.,.) was not >>>> quite to the point. [OTOH, it was to the point, as indeed from >>>> the ?class / ?inherits docu, S3 method dispatch and inherits >>>> must be consistent ] >>>> >>>> > which would break -- unless we change class(x) to return the whole >>>> set of inherited classes, which I sense that we'd rather not do.... >>> >>> [................] >>> >>>> Note again that both "matrix" and "array" are special [see ?class] as >>>> being of __implicit class__ and I am considering that this >>>> implicit class behavior for these two should be slightly >>>> changed .... >>>> >>>> And indeed I think you are right on spot and this would mean >>>> that indeed the implicit class >>>> "matrix" should rather become c("matrix", "array"). >>> >>> I've made up my mind (and not been contradicted by my fellow R >>> corers) to try go there for R 4.0.0 next April. >> >> I can't seem to find the previous thread, so would you mind being a >> bit more explicit here? Do you mean adding "array" to the implicit >> class? > It's late in Europe ;-) > That's my understanding. I think the plan is to have class(matrix()) > return c("matrix", "array"). No class attributes added to matrix or > array objects. > It's all what is needed to have inherits(matrix(), "array") return TRUE > (instead of FALSE at the moment) and S3 dispatch pick up the foo.array > method when foo(matrix()) is called and there is no foo.matrix method. Thank you, Herv?! That's exactly the plan. >> Or adding it to the explicit class? Or adding it to inherits? >> i.e. which of the following results are you proposing to change? >> >> is_array <- function(x) UseMethod("is_array") >> is_array.array <- function(x) TRUE >> is_array.default <- function(x) FALSE >> >> x <- matrix() >> is_array(x) >> #> [1] FALSE >> x <- matrix() >> inherits(x, "array") >> #> [1] FALSE >> class(x) >> #> [1] "matrix" >> >> It would be nice to make sure this is consistent with the behaviour of >> integers, which have an implicit parent class of numeric: > I agree but I don't know if Martin wants to go that far for R 4.0. again, correct. In the mean time, thanks to Tomas Kalibera, my small change has been tested on all of CRAN and Bioc (Software) packages R CMD check <pkg> but no '--as-cran' nor any environment variable settings such as ((strongly recommended by me for package developers !)) _R_CHECK_LENGTH_1_CONDITION_=true _R_CHECK_LENGTH_1_LOGIC2_=verbose>From the package checks, and my own checks I've started noticingonly today, that indeed, the _R_CHECK_LENGTH_1_CONDITION_=true environment variable setting --- stemming more or less directly from an R-devel (mailing list) proposal by Henrik Bengtsson -- and documented in help("if") since R 3.5.0, *together* with the proposal of class(<matrix>) |--> c("matrix", "array") is triggering many new ERRORs because the bad use of class(.) == "..." which I've blogged about is very often inside if(), i.e., if (class(object) == "foobar") # or ` != ` or Now in "new R-devel", and when object is a matrix, if ( class(object) == "foobar") <===> if (c("matrix","array") == "foobar") <===> if (c(FALSE, FALSE)) which is "fine" (i.e, just giving the infamous warning.. which is often surpressed by testthat or similar wrappers) unless you set the env.var .. as I think you R-devel readers all should do : > Sys.unsetenv("_R_CHECK_LENGTH_1_CONDITION_") > if(c(FALSE,FALSE)) 1 else 2 [1] 2 Warning message: In if (c(FALSE, FALSE)) 1 else 2 : the condition has length > 1 and only the first element will be used > Sys.setenv("_R_CHECK_LENGTH_1_CONDITION_" = TRUE) > if(c(FALSE,FALSE)) 1 else 2 Error in if (c(FALSE, FALSE)) 1 else 2 : the condition has length > 1 > > Hopefully that's the longer term plan though (maybe for R 4.1?). I'm not making promises here. Maybe if we could agree to make the equivalent of _R_CHECK_LENGTH_1_CONDITION_=true R 4.0.0's (unconditional?) default behavior, then at least the introduction of class(1L) |--> c("integer", "numeric") would be less problematic because most of the wrong uses of if(class(..) == "integer") would already have been eliminated ... Martin > Note that there are other situations that could follow e.g. > data.frame/list and probably more... > H. >> is_numeric <- function(x) UseMethod("is_numeric") >> is_numeric.numeric <- function(x) TRUE >> is_numeric.default <- function(x) FALSE >> >> x <- 1L >> is_numeric(x) >> #> [1] TRUE >> inherits(x, "numeric") >> #> [1] FALSE >> class(x) >> #> [1] "integer" >> >> Hadley >> > -- > Herv? Pag?s > Program in Computational Biology > Division of Public Health Sciences > Fred Hutchinson Cancer Research Center > 1100 Fairview Ave. N, M1-B514 > P.O. Box 19024 > Seattle, WA 98109-1024 > E-mail: hpages at fredhutch.org > Phone: (206) 667-5791 > Fax: (206) 667-1319
Martin Maechler
2019-Nov-15 16:59 UTC
[Rd] _R_CHECK_LENGTH_1_LOGIC2_ setting and Rstudio ..
>>>>> Martin Maechler >>>>> on Fri, 15 Nov 2019 17:31:15 +0100 writes:> as ((strongly recommended by me for package developers !)) > _R_CHECK_LENGTH_1_CONDITION_=true > _R_CHECK_LENGTH_1_LOGIC2_=verbose Apropos, for many months now, when very occasionally using Rstudio (notably for teaching to beginners), I've needed to use a wrapper shell script *unsetting* these, because otherwise I've got "a bomb" from trying to run Rstudio, as it internally uses code that fails when _R_CHECK_LENGTH_1_LOGIC2_ is activated, see the screen shot (from the very latest Rstudio 1.2.5019), at least in my case where I use a non-trival 'options(repos = ..)' in my startup. -------------- next part -------------- A non-text attachment was scrubbed... Name: rstudio_killed_by_R_CHECK_screenshot.png Type: image/png Size: 63993 bytes Desc: not available URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20191115/5caac0da/attachment.png>
Henrik Bengtsson
2019-Nov-15 17:54 UTC
[Rd] _R_CHECK_LENGTH_1_LOGIC2_ setting and Rstudio ..
Yes, I ran into this too. I think they fixed it for RStudio 1.3 (https://github.com/rstudio/rstudio/pull/5457/files). My workaround is to enable these checks conditionally on not running R in the RStudio Console (it works in the RStudio Terminal). To test for the RStudio Console, you need to turn to .Rprofile rather than .Renviron, e.g. ## Strict run-time checks, unless in the RStudio Console if (Sys.getenv("RSTUDIO") != "1" || nzchar(Sys.getenv("RSTUDIO_TERM")) { Sys.setenv( "_R_CHECK_LENGTH_1_CONDITION_" = "true", "_R_CHECK_LENGTH_1_LOGIC2_" = "verbose" ) } FWIW, for those who use 'startup::startup()` in ~/.Rprofile, the above can also be done by having a file named (exactly) '.Renviron.d/strict,rstudio=FALSE' containing: _R_CHECK_LENGTH_1_CONDITION_=true _R_CHECK_LENGTH_1_LOGIC2_=verbose startup() will only process this file when R is *not* running in the RStudio Console (`rstudio=FALSE`). /Henrik On Fri, Nov 15, 2019 at 9:00 AM Martin Maechler <maechler at stat.math.ethz.ch> wrote:> > >>>>> Martin Maechler > >>>>> on Fri, 15 Nov 2019 17:31:15 +0100 writes: > > > as ((strongly recommended by me for package developers !)) > > > _R_CHECK_LENGTH_1_CONDITION_=true > > _R_CHECK_LENGTH_1_LOGIC2_=verbose > > Apropos, for many months now, when very occasionally using > Rstudio (notably for teaching to beginners), > I've needed to use a wrapper shell script *unsetting* these, > because otherwise I've got "a bomb" from trying to run Rstudio, > as it internally uses code that fails when > _R_CHECK_LENGTH_1_LOGIC2_ > is activated, see the screen shot (from the very latest > Rstudio 1.2.5019), > at least in my case where I use a non-trival 'options(repos = ..)' > in my startup. > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel
Martin Maechler
2019-Nov-21 16:57 UTC
[Rd] class(<matrix>) |--> c("matrix", "arrary") -- and S3 dispatch
TLDR: This is quite technical, still somewhat important: 1) R 4.0.0 will become a bit more coherent: a matrix is an array 2) Your package (or one you use) may be affected.>>>>> Martin Maechler >>>>> on Fri, 15 Nov 2019 17:31:15 +0100 writes:>>>>> Pages, Herve >>>>> on Thu, 14 Nov 2019 19:13:47 +0000 writes:>> On 11/14/19 05:47, Hadley Wickham wrote: >>> On Sun, Nov 10, 2019 at 2:37 AM Martin Maechler ... wrote: [................] >>>>> Note again that both "matrix" and "array" are special [see ?class] as >>>>> being of __implicit class__ and I am considering that this >>>>> implicit class behavior for these two should be slightly >>>>> changed .... >>>>> >>>>> And indeed I think you are right on spot and this would mean >>>>> that indeed the implicit class >>>>> "matrix" should rather become c("matrix", "array"). >>>> >>>> I've made up my mind (and not been contradicted by my fellow R >>>> corers) to try go there for R 4.0.0 next April. >>> I can't seem to find the previous thread, so would you mind being a >>> bit more explicit here? Do you mean adding "array" to the implicit >>> class? >> It's late in Europe ;-) >> That's my understanding. I think the plan is to have class(matrix()) >> return c("matrix", "array"). No class attributes added to matrix or >> array objects. >> It's all what is needed to have inherits(matrix(), "array") return TRUE >> (instead of FALSE at the moment) and S3 dispatch pick up the foo.array >> method when foo(matrix()) is called and there is no foo.matrix method. > Thank you, Herv?! That's exactly the plan. BUT it's wrong what I (and Peter and Herv? and ....) had assumed: If I just change the class (as I already did a few days ago, but you must activate the change via environment variable, see below), S3 dispatch does *NOT* at all pick it up: "matrix" (and "array") are even more special here (see below), and from Hadley's questions, in hindsight I now see that he's been aware of that and I hereby apologize to Hadley for not having thought and looked more, when he asked .. Half an hour ago, I've done another source code commit (svn r77446), to "R-devel" only, of course, and the R-devel NEWS now starts as ------------------------------------------------------------ CHANGES IN R-devel: USER-VISIBLE CHANGES: ? .... intention that the next non-patch release should be 4.0.0. ? R now builds by default against a PCRE2 library ........ ................... ................... ? For now only active when environment variable _R_CLASS_MATRIX_ARRAY_ is set to non-empty, but planned to be the new unconditional behavior when R 4.0.0 is released: Newly, matrix objects also inherit from class "array", namely, e.g., class(diag(1)) is c("matrix", "array") which invalidates code (wrongly) assuming that length(class(obj)) == 1, a wrong assumption that is less frequently fulfilled now. (Currently only after setting _R_CLASS_MATRIX_ARRAY_ to non-empty.) S3 methods for "array", i.e., <someFun>.array(), are now also dispatched for matrix objects. ------------------------------------------------------------ (where only the very last 1.5 lines paragraph is new.) Note the following (if you use a version of R-devel, with svn rev >= 77446; which you may get as a binary for Windows in about one day; everyone else needs to compile for the sources .. or wait a bit, maybe also not much longer than one day, for a docker image) :> Sys.unsetenv("_R_CLASS_MATRIX_ARRAY_") # ==> current R behavior > class(m <- diag(1))[1] "matrix"> Sys.setenv("_R_CLASS_MATRIX_ARRAY_" = "BOOH !") # ==> future R behavior > class(m)[1] "matrix" "array"> > foo <- function(x) UseMethod("foo") > foo.array <- function(x) "made in foo.array()" > foo(m)[1] "made in foo.array()"> Sys.unsetenv("_R_CLASS_MATRIX_ARRAY_")# ==> current R behavior > foo(m)Error in UseMethod("foo") : no applicable method for 'foo' applied to an object of class "c('matrix', 'double', 'numeric')"> Sys.setenv("_R_CLASS_MATRIX_ARRAY_" = TRUE) # ==> future R behavior > foo(m)[1] "made in foo.array()"> foo.A <- foo.array ; rm(foo.array) > foo(m)Error in UseMethod("foo") : no applicable method for 'foo' applied to an object of class "c('matrix', 'array', 'double', 'numeric')">So, with my commit 77446, the _R_CLASS_MATRIX_ARRAY_ environment variable also changes the "S3 dispatch determining class" mentioned as 'class' in the error message (of the two cases, old and new) above, which in R <= 3.6.x for a numeric matrix is c('matrix', 'double', 'numeric') and from R 4.0.0 on will be c('matrix', 'array', 'double', 'numeric') Note that this is *not* (in R <= 3.6.x, nor very probably in R 4.0.0) the same as R's class(). Hadley calls this long class vector the 'implicit class' -- which is a good term but somewhat conflicting with R's (i.e. R-core's) "definition" used in the ?class help page (for ca. 11 years). R's internal C code has a nice function class R_data_class2() which computes this 'S3-dispatch-class' character (vector) for any R object, and R_data_class2() is indeed called from (the underlying C function of) R's UseMethod(). Using the above fact of an error message, I wrote a nice (quite well tested) function my.class2() which returns this S3_dispatch_class() also in current versions of R: my.class2 <- function(x) { # use a fn name not used by any sane .. foo.7.3.343 <- function(x) UseMethod("foo.7.3.343") msg <- tryCatch(foo.7.3.343(x), error=function(e) e$message) clm <- sub('"$', '', sub(".* of class \"", '', msg)) if(is.language(x) || is.function(x)) clm else { cl <- str2lang(clm) if(is.symbol(cl)) as.character(cl) else eval(cl) } } ## str2lang() needs R >= 3.6.0: if(getRversion() < "3.6.0") ## substitute for str2lang(), good enough here: str2lang <- function(s) parse(text = s, keep.source=FALSE)[[1]] Now you can look at such things yourself: ## --------------------- the "interesting" cases : --- ## integer and double my.class2( pi) # == c("double", "numeric") my.class2(1:2) # == c("integer", "numeric") ## matrix and array [also combined with int / double ] : my.class2(matrix(1L, 2,3)) # == c(matrixCL, "integer", "numeric") <<< my.class2(matrix(pi, 2,3)) # == c(matrixCL, "double", "numeric") <<< my.class2(array("A", 2:3)) # == c(matrixCL, "character") <<< my.class2(array(1:24, 2:4)) # == c("array", "integer", "numeric") my.class2(array( pi , 2:4)) # == c("array", "double", "numeric") my.class2(array(TRUE, 2:4)) # == c("array", "logical") my.class2(array(letters, 2:4)) # == c("array", "character") my.class2(array(1:24 + 1i, 2)) # == c("array", "complex") ## other cases my.class2(NA) # == class(NA) : "logical" my.class2("A") # == class("B"): "character" my.class2(as.raw(0:2)) # == "raw" my.class2(1 + 2i) # == "complex" my.class2(USJudgeRatings)#== "data.frame" my.class2(class) # == "function" # also for a primitive my.class2(globalenv()) # == "environment" my.class2(quote(sin(x)))# == "call" my.class2(quote(sin) ) # == "name" my.class2(quote({})) # == class(*) == "{" my.class2(quote((.))) # == class(*) == "(" ----------------------------------------------------- note that of course, the lines marked "<<<" above, contain 'matrixCL' which is "matrix" in "old" (i.e. current) R, and is c("matrix", "array") in "new" (i.e. future) R. Last but not least: It's quite trivial (only few words need to be added to the sources; more to the documentation) to add an R function to base R which provides the same as my.class2() above, (but much more efficiently, not via catching error messages !!), and my current proposal for that function's name is .class2() {it should start with a dot ("."), as it's not for the simple minded average useR ... and you know how I'm happy with function names that do not need one single [Shift] key ...} The current plan contains 1) Notify CRAN package maintainers (ca 140) whose packages no longer pass R CMD check when the feature is turned on (via setting the environment variable) in R-devel. 2a) (Some) CRAN team members set _R_CLASS_MATRIX_ARRAY_ (to non-empty), as part of the incoming checks, at least for all new CRAN submissions 2b) set the _R_CLASS_MATRIX_ARRAY_ (to non-empty), as part of ' R CMD check --as-cran <pkg>' 3) Before the end of 2019, change the R sources (for R-devel) such that it behaves as it behaves currently when the environment variable is set *AND* abolish this environment variable from the sources. {read on to learn *why*} Consequently (to 3), R 4.0.0 will behave as indicated, unconditionally. Note that (as I've shown above in the first example set) this is set up in such a manner that you can change the environment variable during a *running* R session, and observe the effect immediately. This however lead to some slow down of quite a bit of the R code, because actually the environment variable has to be checked quite often (easily dozens of times for simple R calls). For that reason, we want to do "3)" as quickly as possible. Please do not hesitate to ask or comment -- here, not on Twitter, please -- noting that I'll be basically offline for an extended weekend within 24h, now. I hope this will eventually to lead to clean up and clarity in R, and hence should be worth the pain of broken back-compatibility and having to adapt your (almost always only sub-optimally written ;-)) R code, see also my Blog http://bit.ly/R_blog_class_think_2x Martin Maechler ETH Zurich and R Core team
Dirk Eddelbuettel
2019-Nov-25 17:01 UTC
[Rd] class(<matrix>) |--> c("matrix", "arrary") -- and S3 dispatch
On 21 November 2019 at 17:57, Martin Maechler wrote: | (if you use a version of R-devel, with svn rev >= 77446; which | you may get as a binary for Windows in about one day; everyone | else needs to compile for the sources .. or wait a bit, maybe | also not much longer than one day, for a docker image) : FYI: rocker/drd [1] and rocker/r-devel both have rev 77455 now (as they are both on weekend auto-rebuild schedule). The former is smaller, both should work to test this. Quick demo below [2]. Dirk [1] This comes from 'drd == daily r-devel' but we do not build it daily. [2] Quick demo follows edd at rob:~$ docker run --rm -ti rocker/r-devel bash root at a30e4a5c89ba:/# RD R Under development (unstable) (2019-11-23 r77455) -- "Unsuffered Consequences" Copyright (C) 2019 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. Natural language support but running in an English locale R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R.> Sys.setenv("_R_CLASS_MATRIX_ARRAY_" = "BOOH !") # ==> future R behavior > class(m <- diag(1))[1] "matrix" "array">-- http://dirk.eddelbuettel.com | @eddelbuettel | edd at debian.org
Pages, Herve
2020-Jan-21 17:33 UTC
[Rd] class(<matrix>) |--> c("matrix", "arrary") -- and S3 dispatch
Dear Martin, What's the ETA for _R_CLASS_MATRIX_ARRAY_=TRUE to become the new unconditional behavior in R devel? Thanks! H. On 11/21/19 08:57, Martin Maechler wrote:> > TLDR: This is quite technical, still somewhat important: > 1) R 4.0.0 will become a bit more coherent: a matrix is an array > 2) Your package (or one you use) may be affected. > > >>>>>> Martin Maechler >>>>>> on Fri, 15 Nov 2019 17:31:15 +0100 writes: > >>>>>> Pages, Herve >>>>>> on Thu, 14 Nov 2019 19:13:47 +0000 writes: > > >> On 11/14/19 05:47, Hadley Wickham wrote: > >>> On Sun, Nov 10, 2019 at 2:37 AM Martin Maechler ... wrote: > > [................] > > >>>>> Note again that both "matrix" and "array" are special [see ?class] as > >>>>> being of __implicit class__ and I am considering that this > >>>>> implicit class behavior for these two should be slightly > >>>>> changed .... > >>>>> > >>>>> And indeed I think you are right on spot and this would mean > >>>>> that indeed the implicit class > >>>>> "matrix" should rather become c("matrix", "array"). > >>>> > >>>> I've made up my mind (and not been contradicted by my fellow R > >>>> corers) to try go there for R 4.0.0 next April. > > >>> I can't seem to find the previous thread, so would you mind being a > >>> bit more explicit here? Do you mean adding "array" to the implicit > >>> class? > > >> It's late in Europe ;-) > > >> That's my understanding. I think the plan is to have class(matrix()) > >> return c("matrix", "array"). No class attributes added to matrix or > >> array objects. > > >> It's all what is needed to have inherits(matrix(), "array") return TRUE > >> (instead of FALSE at the moment) and S3 dispatch pick up the foo.array > >> method when foo(matrix()) is called and there is no foo.matrix method. > > > Thank you, Herv?! That's exactly the plan. > > BUT it's wrong what I (and Peter and Herv? and ....) had assumed: > > If I just change the class > (as I already did a few days ago, but you must activate the change > via environment variable, see below), > > S3 dispatch does *NOT* at all pick it up: > "matrix" (and "array") are even more special here (see below), > and from Hadley's questions, in hindsight I now see that he's been aware > of that and I hereby apologize to Hadley for not having thought > and looked more, when he asked .. > > Half an hour ago, I've done another source code commit (svn r77446), > to "R-devel" only, of course, and the R-devel NEWS now starts as > > ------------------------------------------------------------ > > CHANGES IN R-devel: > > USER-VISIBLE CHANGES: > > ? .... intention that the next non-patch release should be 4.0.0. > > ? R now builds by default against a PCRE2 library ........ > ................... > ................... > > ? For now only active when environment variable > _R_CLASS_MATRIX_ARRAY_ is set to non-empty, but planned to be the > new unconditional behavior when R 4.0.0 is released: > > Newly, matrix objects also inherit from class "array", namely, > e.g., class(diag(1)) is c("matrix", "array") which invalidates > code (wrongly) assuming that length(class(obj)) == 1, a wrong > assumption that is less frequently fulfilled now. (Currently > only after setting _R_CLASS_MATRIX_ARRAY_ to non-empty.) > > S3 methods for "array", i.e., <someFun>.array(), are now also > dispatched for matrix objects. > > ------------------------------------------------------------ > (where only the very last 1.5 lines paragraph is new.) > > Note the following > (if you use a version of R-devel, with svn rev >= 77446; which > you may get as a binary for Windows in about one day; everyone > else needs to compile for the sources .. or wait a bit, maybe > also not much longer than one day, for a docker image) : > > >> Sys.unsetenv("_R_CLASS_MATRIX_ARRAY_") # ==> current R behavior >> class(m <- diag(1)) > [1] "matrix" >> Sys.setenv("_R_CLASS_MATRIX_ARRAY_" = "BOOH !") # ==> future R behavior >> class(m) > [1] "matrix" "array" >> >> foo <- function(x) UseMethod("foo") >> foo.array <- function(x) "made in foo.array()" >> foo(m) > [1] "made in foo.array()" >> Sys.unsetenv("_R_CLASS_MATRIX_ARRAY_")# ==> current R behavior >> foo(m) > Error in UseMethod("foo") : > no applicable method for 'foo' applied to an object of class "c('matrix', 'double', 'numeric')" > >> Sys.setenv("_R_CLASS_MATRIX_ARRAY_" = TRUE) # ==> future R behavior >> foo(m) > [1] "made in foo.array()" >> foo.A <- foo.array ; rm(foo.array) >> foo(m) > Error in UseMethod("foo") : > no applicable method for 'foo' applied to an object of class "c('matrix', 'array', 'double', 'numeric')" >> > > So, with my commit 77446, the _R_CLASS_MATRIX_ARRAY_ > environment variable also changes the > > "S3 dispatch determining class" > > mentioned as 'class' in the error message (of the two cases, old > and new) above, which in R <= 3.6.x for a numeric matrix is > > c('matrix', 'double', 'numeric') > > and from R 4.0.0 on will be > > c('matrix', 'array', 'double', 'numeric') > > Note that this is *not* (in R <= 3.6.x, nor very probably in R 4.0.0) > the same as R's class(). > Hadley calls this long class vector the 'implicit class' -- which > is a good term but somewhat conflicting with R's (i.e. R-core's) > "definition" used in the ?class help page (for ca. 11 years). > > R's internal C code has a nice function class R_data_class2() > which computes this 'S3-dispatch-class' character (vector) for > any R object, and R_data_class2() is indeed called from (the > underlying C function of) R's UseMethod(). > > Using the above fact of an error message, > I wrote a nice (quite well tested) function my.class2() which > returns this S3_dispatch_class() also in current versions of R: > > my.class2 <- function(x) { # use a fn name not used by any sane .. > foo.7.3.343 <- function(x) UseMethod("foo.7.3.343") > msg <- tryCatch(foo.7.3.343(x), error=function(e) e$message) > clm <- sub('"$', '', sub(".* of class \"", '', msg)) > if(is.language(x) || is.function(x)) > clm > else { > cl <- str2lang(clm) > if(is.symbol(cl)) as.character(cl) else eval(cl) > } > } > > ## str2lang() needs R >= 3.6.0: > if(getRversion() < "3.6.0") ## substitute for str2lang(), good enough here: > str2lang <- function(s) parse(text = s, keep.source=FALSE)[[1]] > > > Now you can look at such things yourself: > > ## --------------------- the "interesting" cases : --- > ## integer and double > my.class2( pi) # == c("double", "numeric") > my.class2(1:2) # == c("integer", "numeric") > ## matrix and array [also combined with int / double ] : > my.class2(matrix(1L, 2,3)) # == c(matrixCL, "integer", "numeric") <<< > my.class2(matrix(pi, 2,3)) # == c(matrixCL, "double", "numeric") <<< > my.class2(array("A", 2:3)) # == c(matrixCL, "character") <<< > my.class2(array(1:24, 2:4)) # == c("array", "integer", "numeric") > my.class2(array( pi , 2:4)) # == c("array", "double", "numeric") > my.class2(array(TRUE, 2:4)) # == c("array", "logical") > my.class2(array(letters, 2:4)) # == c("array", "character") > my.class2(array(1:24 + 1i, 2)) # == c("array", "complex") > > ## other cases > my.class2(NA) # == class(NA) : "logical" > my.class2("A") # == class("B"): "character" > my.class2(as.raw(0:2)) # == "raw" > my.class2(1 + 2i) # == "complex" > my.class2(USJudgeRatings)#== "data.frame" > my.class2(class) # == "function" # also for a primitive > my.class2(globalenv()) # == "environment" > my.class2(quote(sin(x)))# == "call" > my.class2(quote(sin) ) # == "name" > my.class2(quote({})) # == class(*) == "{" > my.class2(quote((.))) # == class(*) == "(" > > ----------------------------------------------------- > > note that of course, the lines marked "<<<" above, contain > 'matrixCL' which is "matrix" in "old" (i.e. current) R, > and is c("matrix", "array") in "new" (i.e. future) R. > > Last but not least: It's quite trivial (only few words need to > be added to the sources; more to the documentation) to add an R > function to base R which provides the same as my.class2() above, > (but much more efficiently, not via catching error messages !!), > and my current proposal for that function's name is .class2() > {it should start with a dot ("."), as it's not for the simple > minded average useR ... and you know how I'm happy with > function names that do not need one single [Shift] key ...} > > The current plan contains > > 1) Notify CRAN package maintainers (ca 140) whose packages no > longer pass R CMD check when the feature is turned on > (via setting the environment variable) in R-devel. > > 2a) (Some) CRAN team members set _R_CLASS_MATRIX_ARRAY_ (to non-empty), > as part of the incoming checks, at least for all new CRAN submissions > > 2b) set the _R_CLASS_MATRIX_ARRAY_ (to non-empty), as part of > ' R CMD check --as-cran <pkg>' > > 3) Before the end of 2019, change the R sources (for R-devel) > such that it behaves as it behaves currently when the environment > variable is set *AND* abolish this environment variable from > the sources. {read on to learn *why*} > > Consequently (to 3), R 4.0.0 will behave as indicated, unconditionally. > > Note that (as I've shown above in the first example set) this is > set up in such a manner that you can change the environment > variable during a *running* R session, and observe the effect immediately. > This however lead to some slow down of quite a bit of the R > code, because actually the environment variable has to be > checked quite often (easily dozens of times for simple R calls). > > For that reason, we want to do "3)" as quickly as possible. > > Please do not hesitate to ask or comment > -- here, not on Twitter, please -- noting that I'll be > basically offline for an extended weekend within 24h, now. > > I hope this will eventually to lead to clean up and clarity in > R, and hence should be worth the pain of broken > back-compatibility and having to adapt your (almost always only > sub-optimally written ;-)) R code, > see also my Blog https://urldefense.proofpoint.com/v2/url?u=http-3A__bit.ly_R-5Fblog-5Fclass-5Fthink-5F2x&d=DwIDaQ&c=eRAMFD45gAfqt84VtBcfhQ&r=BK7q3XeAvimeWdGbWY_wJYbW0WYiZvSXAJJKaaPhzWA&m=xAGXmo1FhJxT-qBfj-McDEn3sqWhqJHNV-IPpN7g6oA&s=yUUwdjl5LE90V0tLTM3FZYZ0zHf8coHo49Vt95O7IwQ&e> > Martin Maechler > ETH Zurich and R Core team >-- Herv? Pag?s Program in Computational Biology Division of Public Health Sciences Fred Hutchinson Cancer Research Center 1100 Fairview Ave. N, M1-B514 P.O. Box 19024 Seattle, WA 98109-1024 E-mail: hpages at fredhutch.org Phone: (206) 667-5791 Fax: (206) 667-1319
Possibly Parallel Threads
- class(<matrix>) |--> c("matrix", "arrary") [was "head.matrix ..."]
- class(<matrix>) |--> c("matrix", "arrary") -- and S3 dispatch
- class(<matrix>) |--> c("matrix", "arrary") -- and S3 dispatch
- binary form of is() contradicts its unary form
- binary form of is() contradicts its unary form