From m@ech|er @end|ng |rom @t@t@m@th@ethz@ch  Fri Nov 15 17:31:15 2019
From: m@ech|er @end|ng |rom @t@t@m@th@ethz@ch (Martin Maechler)
Date: Fri, 15 Nov 2019 17:31:15 +0100
Subject: [Rd] class(<matrix>) |--> c("matrix",
 "arrary") [was "head.matrix ..."]
In-Reply-To: <52b343f6-0861-ac16-4eff-c91d17501843@fredhutch.org>
References: <CAPRVBcxsAkcgr3mDc4skTe8DTxJaYe2xyhk3QMk3odW1MGcyNA@mail.gmail.com>
 <CAPRVBczYZHO3JaFVfNeNNV0-YMRvX3CUYhUvLCjophDEoP+XgQ@mail.gmail.com>
 <23935.14449.965488.684663@stat.math.ethz.ch>
 <CAPRVBcwMY=GAVfFeCwowQsxP31Y=_B2ducosThN+8V91WESB8Q@mail.gmail.com>
 <20449_1568703158_x8H6qIDG009669_f8285123-371f-5d4b-1281-6cc085dba122@fredhutch.org>
 <22204_1568723392_x8HCSG3F015456_674779E0-B1F6-4DC2-9F1C-D5D48B044B66@mcmaster.ca>
 <AD725242-0FB7-4004-9F16-304E345A363A@mcmaster.ca>
 <23937.1791.389379.103733@stat.math.ethz.ch>
 <CAD4oTHF_e4rtHu0S5bUu38+kQSEHXwun3Sv9-CbK+xN9tSc2hA@mail.gmail.com>
 <CAD4oTHFf7Zp+Xe3jvv7oirLWgZm7ZiSNWZvbrEsqJGfrL6Q2wQ@mail.gmail.com>
 <23993.29737.690600.313456@stat.math.ethz.ch>
 <38105A28-07AE-48F6-A4A4-0C1FCCCBFBDC@gmail.com>
 <23995.62027.969336.196344@stat.math.ethz.ch>
 <CAD4oTHF1+PdJX-Qq=JZwafhGNatDu9AZVD0prCAvyJFL9bLzVg@mail.gmail.com>
 <24007.52264.596794.767557@stat.math.ethz.ch>
 <CABdHhvEzVtwajH3fRD_ZOzChf6kVr2j3j6dD-Uo2i92Vj9kTtQ@mail.gmail.com>
 <52b343f6-0861-ac16-4eff-c91d17501843@fredhutch.org>
Message-ID: <24014.53971.84201.964445@stat.math.ethz.ch>

>>>>> 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 noticing
only 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



[Rd] class(<matrix>) |--> c("matrix", "arrary") [was "head.matrix ..."]
Martin Maechler