Re: Discrepancy between is.list() and is(x, "list")

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Abby Spurdle
> I have noticed a discrepancy between is.list() and is(x, “list”)

There's a similar problem with inherits().

On R 3.5.3:

> f = function () 1
> class (f) = "f"

> is.function (f)
[1] TRUE
> inherits (f, "function")
[1] FALSE

I didn't check what happens with:
> class (f) = c ("f", "function")

However, they should have the same result, regardless.

> Is this discrepancy intentional?

I hope not.

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Berry, Charles
In the case of inherits (at least) this seems intended.

The help page says:

"If the object does not have a class attribute, it has an implicit class..."

which I take to mean that if an object does have a class attribute it does not also have an implicit class.

The behavior you noted below will apply to other types bearing implicit classes. For example:

> inherits(1.0, "numeric")
[1] TRUE
> inherits(structure(1.0, class="myclass"), "numeric")
[1] FALSE
>

I think this is reasonable behavior. Consider the "Date" class, which stores values as "numeric":

> class(Sys.Date())
[1] "Date"
> inherits(Sys.Date(),"numeric")
[1] FALSE
> class(unclass(Sys.Date()))
[1] "numeric"
> Sys.Date()%%2
Error in Ops.Date(Sys.Date(), 2) : %% not defined for "Date" objects
>

Letting the modulus operator (as one example) inherit the numeric class here could create problems.

Of course for classes that should inherit the implicit type, it can be explicitly added to the end of the class() vector by its constructor.

HTH,

Chuck



> On Mar 25, 2019, at 8:27 PM, Abs Spurdle <[hidden email]> wrote:
>
>> I have noticed a discrepancy between is.list() and is(x, “list”)
>
> There's a similar problem with inherits().
>
> On R 3.5.3:
>
>> f = function () 1
>> class (f) = "f"
>
>> is.function (f)
> [1] TRUE
>> inherits (f, "function")
> [1] FALSE
>
> I didn't check what happens with:
>> class (f) = c ("f", "function")
>
> However, they should have the same result, regardless.
>
>> Is this discrepancy intentional?
>
> I hope not.
>
> [[alternative HTML version deleted]]
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Rui Barradas
Hello,

Here is another example.

df1 <- data.frame(a = 1:3, b = 4:6)

inherits(df1, "data.frame")
#[1] TRUE
class(df1)
#[1] "data.frame"

inherits(df1, "list")
#[1] FALSE


This is documented behavior, the help page ?inherits says

The function class prints the vector of names of classes an object
inherits from.


So far, so good. But now comes the part I don't like.

is.list(df1)
#[1] TRUE


Strictly speaking this is not unexpected behavior (because it's
documented) but isn't it *inconsistent* behavior?


Rui Barradas

Às 16:30 de 26/03/2019, Berry, Charles escreveu:

> In the case of inherits (at least) this seems intended.
>
> The help page says:
>
> "If the object does not have a class attribute, it has an implicit class..."
>
> which I take to mean that if an object does have a class attribute it does not also have an implicit class.
>
> The behavior you noted below will apply to other types bearing implicit classes. For example:
>
>> inherits(1.0, "numeric")
> [1] TRUE
>> inherits(structure(1.0, class="myclass"), "numeric")
> [1] FALSE
>>
>
> I think this is reasonable behavior. Consider the "Date" class, which stores values as "numeric":
>
>> class(Sys.Date())
> [1] "Date"
>> inherits(Sys.Date(),"numeric")
> [1] FALSE
>> class(unclass(Sys.Date()))
> [1] "numeric"
>> Sys.Date()%%2
> Error in Ops.Date(Sys.Date(), 2) : %% not defined for "Date" objects
>>
>
> Letting the modulus operator (as one example) inherit the numeric class here could create problems.
>
> Of course for classes that should inherit the implicit type, it can be explicitly added to the end of the class() vector by its constructor.
>
> HTH,
>
> Chuck
>
>
>
>> On Mar 25, 2019, at 8:27 PM, Abs Spurdle <[hidden email]> wrote:
>>
>>> I have noticed a discrepancy between is.list() and is(x, “list”)
>>
>> There's a similar problem with inherits().
>>
>> On R 3.5.3:
>>
>>> f = function () 1
>>> class (f) = "f"
>>
>>> is.function (f)
>> [1] TRUE
>>> inherits (f, "function")
>> [1] FALSE
>>
>> I didn't check what happens with:
>>> class (f) = c ("f", "function")
>>
>> However, they should have the same result, regardless.
>>
>>> Is this discrepancy intentional?
>>
>> I hope not.
>>
>> [[alternative HTML version deleted]]
>>
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Abby Spurdle
In reply to this post by Berry, Charles
If I can merge this thread with the one I started yesterday...

> "If the object does not have a class attribute, it has an implicit
class..."
> which I take to mean that if an object does have a class attribute it
does not also have an implicit class.
> I think this is reasonable behavior. Consider the "Date" class, which
stores values as "numeric":
> > class(Sys.Date())
> [1] "Date"
> > inherits(Sys.Date(),"numeric")
> [1] FALSE
> > class(unclass(Sys.Date()))
> [1] "numeric"
> > Sys.Date()%%2
> Error in Ops.Date(Sys.Date(), 2) : %% not defined for "Date" objects
> Letting the modulus operator (as one example) inherit the numeric class
here could create problems.

I disagree.
A date object should probably extend integers rather than numerics, in the
first place.
However, if it extends numeric, then it extends numeric, otherwise it's a
contradiction.
So, inherits(Sys.Date(),"numeric") should return true.

Modulo operators should be defined for both dates and numerics.
However, the application of modulo operators to dates, is perhaps unclear,
at least in the general case, anyway.

> so instead of hitting utils:::head.function, it hits utils:::head.default
> I also see this behavior at least as far aback as 3.5.1, so its not new
to 3.5.3.

These seem like significant design flaws.
Implicit classes or whatever you want to call them, are clearly part of the
class hierarchy.

They should be included in inherits(), is() and standard method dispatch,
regardless of whether they are part of the class vector or not.

Also, is this something that was introduced in R 3.5.1?
The only thing worse than a design flaw is a design flaw that isn't
backward compatible.

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

R devel mailing list
I think this goes back to SV4 (c. late 1990's).  The is.<type>  functions
are much older (c. mid 1970's) , from before any class system was in S.
is() and inherits() were introduced with the S4 class system and were meant
to escape from the prison made by ancient design choices.

Bill Dunlap
TIBCO Software
wdunlap tibco.com


On Tue, Mar 26, 2019 at 2:11 PM Abs Spurdle <[hidden email]> wrote:

> If I can merge this thread with the one I started yesterday...
>
> > "If the object does not have a class attribute, it has an implicit
> class..."
> > which I take to mean that if an object does have a class attribute it
> does not also have an implicit class.
> > I think this is reasonable behavior. Consider the "Date" class, which
> stores values as "numeric":
> > > class(Sys.Date())
> > [1] "Date"
> > > inherits(Sys.Date(),"numeric")
> > [1] FALSE
> > > class(unclass(Sys.Date()))
> > [1] "numeric"
> > > Sys.Date()%%2
> > Error in Ops.Date(Sys.Date(), 2) : %% not defined for "Date" objects
> > Letting the modulus operator (as one example) inherit the numeric class
> here could create problems.
>
> I disagree.
> A date object should probably extend integers rather than numerics, in the
> first place.
> However, if it extends numeric, then it extends numeric, otherwise it's a
> contradiction.
> So, inherits(Sys.Date(),"numeric") should return true.
>
> Modulo operators should be defined for both dates and numerics.
> However, the application of modulo operators to dates, is perhaps unclear,
> at least in the general case, anyway.
>
> > so instead of hitting utils:::head.function, it hits utils:::head.default
> > I also see this behavior at least as far aback as 3.5.1, so its not new
> to 3.5.3.
>
> These seem like significant design flaws.
> Implicit classes or whatever you want to call them, are clearly part of the
> class hierarchy.
>
> They should be included in inherits(), is() and standard method dispatch,
> regardless of whether they are part of the class vector or not.
>
> Also, is this something that was introduced in R 3.5.1?
> The only thing worse than a design flaw is a design flaw that isn't
> backward compatible.
>
>         [[alternative HTML version deleted]]
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Gabriel Becker-2
In reply to this post by Abby Spurdle
Hi Abs,

Lets try to remain civil even when disagreeing about major design
philosophies, ok?

On Tue, Mar 26, 2019 at 2:08 PM Abs Spurdle <[hidden email]> wrote:

> If I can merge this thread with the one I started yesterday...
>
> > "If the object does not have a class attribute, it has an implicit
> class..."
> > which I take to mean that if an object does have a class attribute it
> does not also have an implicit class.
> > I think this is reasonable behavior. Consider the "Date" class, which
> stores values as "numeric":
> > > class(Sys.Date())
> > [1] "Date"
> > > inherits(Sys.Date(),"numeric")
> > [1] FALSE
> > > class(unclass(Sys.Date()))
> > [1] "numeric"
> > > Sys.Date()%%2
> > Error in Ops.Date(Sys.Date(), 2) : %% not defined for "Date" objects
> > Letting the modulus operator (as one example) inherit the numeric class
> here could create problems.
>
> I disagree.
> A date object should probably extend integers rather than numerics, in the
> first place.
> However, if it extends numeric, then it extends numeric, otherwise it's a
> contradiction.
> So, inherits(Sys.Date(),"numeric") should return true.
>

You seem to be approaching the S3 "class"/dispatch system as something that
it is not: a formal class system. S3 dispatch is based, essentially, on
labeling, via the class attribute (or, if you like, the value returned by
class(), this is basically the same with some fiddly bits for S4, judging
by a quick glance at src/main/attrib.c:do_class ). If it is not in the set
of class labels, S3 dispatch *will not* treat it as that class. This is by
design. An S3 object's "class" also has no* bearing on the contents of the
object (* this isn't true for some built in atomic vector classes, as I
Recall, but it is for all user defined classes).


> mylist = list("hi", "what?")

> class(mylist) = "Date"

> mylist

[1] NA NA

*Warning messages:*

*1: In as.POSIXlt.Date(x) : NAs introduced by coercion*


*2: In as.POSIXlt.Date(x) : NAs introduced by coercion*


See? the print method looks at the class attribute of mylist, and says "Oh,
this is a Date, I'll use print.Date" and then it craps out (with NAs
instead of errors, but still) because the data contained within the object
isn't really a "Date". But there is no definition of what it means to be an
S3 "Date" object anywhere, other than that it has the Date class attribute.

All of the above is *by design*. You're welcome to not like the design. I
prefer the formalism of S4, myself, and there are various things I don't
love about how S3 works - including some but not all of the things that
have come up here. That said, the behaviors are not bugs, and any of the
changes you seem to be advocating for would not only break lots of code,
but would also represent fundamental changes to the design of a core aspect
of the R language.

As for doing %% on Dates, I seriously doubt anyone who is operating on Date
objects and thinking about times and dates is looking to do a modulo
operation in number of Days since Jan 1st 1970, which is what that
operation would do. Better to have that fail and if that really, for sure
is what the user actually wants to do, they can uncalss it or otherwise
convert it to a numeridc first.



> Modulo operators should be defined for both dates and numerics.
> However, the application of modulo operators to dates, is perhaps unclear,
> at least in the general case, anyway.
>
> > so instead of hitting utils:::head.function, it hits
> utils:::head.default
> > I also see this behavior at least as far aback as 3.5.1, so its not new
> to 3.5.3.
>

> These seem like significant design flaws.
> Implicit classes or whatever you want to call them, are clearly part of
> the class hierarchy.
>

I'm not sure what is clear about that, or what class hierarchy you're
talking about in the S3 case. Remember that S3 classes *have no formal
definitions at all*. Thats why I rarely if ever use them in software that I
write. But it's an important point here. What something would becomes if
you unclass()ed it has no bearing on what S3 dispatch will do.


>
> They should be included in inherits(), is() and standard method dispatch,
> regardless of whether they are part of the class vector or not.
>

Dispatch is ONLY done on the class vector for S3 (AFAIK). Only. That is how
S3 dispatch is defined and designed.

>
> Also, is this something that was introduced in R 3.5.1?
> The only thing worse than a design flaw is a design flaw that isn't
> backward compatible.
>

No that is just the non-devel R I had handy, and you had seemed to be
presenting it as something new in 3.5.3. I would be surprised if the
behavior doesn't go all the way back to whenever head.function was added.
Before that head() on a function (likely) would have failed just like it
still does on your reclassed function. Because, again, this is how S3 is
supposed to behave when you give it the inputs you are. I fyou want your f
class to hit function S3 methods, you need to do

class(myfun) <- c("f", "function")

Then everything will work.

Best,
~G

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Abby Spurdle
> you had seemed to be presenting it as something new in 3.5.3. I would be
surprised if the behavior doesn't go all the way back to whenever
head.function was added.

My bad.
I'm just surprised I've never noticed these problems before.

> S3 classes have no formal definitions at all
> I'm not sure what is clear about that, or what class hierarchy you're
talking about in the S3 case.

That's questionable.
One, because it depends on how you define formal definitions.
And two, because class definitions can exist outside the code itself.
e.g. As part of an object oriented model.
(Being "Object Oriented" is just as much about models as it is about
syntax).

Furthermore, when you change the class of a vector, list or function, much
of the original object's structure and behavior remains.
So, it has "Inherited" or "Extended", in my opinion.
Resulting in a class hierarchy.

> Dispatch is ONLY done on the class vector for S3 (AFAIK)

Incorrect.
We've already mentioned the example of head.function().
In general, this dispatch occurs without the presence of a class attribute.

> You seem to be approaching the S3 "class"/dispatch system as something
that it is not: a formal class system

If I can diverge...

Where did this term "Formal Class System" come from?
I've never seen it used anywhere else.
Is is specific to R?

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Gabriel Becker-2
Abs et al,

Ok, so I have just gone and re-read the docs again. My language was a more
absolute than it should have been; *however*, I was still correct for the
cases under discussion.

From ?UseMethod (emphasis mine)

An R object is a data object which has a ‘class’ attribute (and

     this can be tested by ‘is.object’).  A class attribute is a

     character vector giving the names of the classes from which the

     object _inherits_.  *If the object does not have a class attribute,*

*     it has an implicit class.*  Matrices and arrays have class

     ‘"matrix"’ or‘"array"’ followed by the class of the underlying

     vector.  Most vectors have class the result of ‘mode(x)’, except

     that integer vectors have class ‘c("integer", "numeric")’ and real

     vectors have class ‘c("double", "numeric")’.


So, there are implicit classes, but *only when the data object is NOT an "R
object" (ie when it does NOT have a class attribute).* So, what I said was
not correct for certain built in classes: matrices, arrays, and some
atomic vectors but IS true of any object you assign a class attribute to
(e.g. by doing class<-() ) When your code classes an object, you have to
give the full desired vector of inheritence in its class attribute.
Anything you leave out just won't be there.

This is the case with your "f" classed object, so it *does not have an
implicit class.*  That is how S3 is designed and intended to work.


On Tue, Mar 26, 2019 at 6:42 PM Abs Spurdle <[hidden email]> wrote:

> > you had seemed to be presenting it as something new in 3.5.3. I would be
> surprised if the behavior doesn't go all the way back to whenever
> head.function was added.
>
> My bad.
> I'm just surprised I've never noticed these problems before.
>
> > S3 classes have no formal definitions at all
> > I'm not sure what is clear about that, or what class hierarchy you're
> talking about in the S3 case.
>
> That's questionable.
> One, because it depends on how you define formal definitions.
>

I mean, I guess, in the sense that that is true of any argument anyone ever
makes that uses a term.

For the record here, I'm using "Formal class definition" as an explicit
declaration of how valid objects of a particular class are structured, what
data they contain, and how they behave.

S3 does not have that. IT has only *implicit* class structure/content
definitions based on what methods for that class look for in objects passed
to them. There is no where you can look to figure out what it "means to be
a ____ class object" in the S3 sense, beyond a nebulous set of methods
which look for various things within an object that supposedly is of that
class.

> x = "not a date"

> class(x) = "Date"

> is(x, "Date")

[1] TRUE


> y = Sys.Date()

> y

[1] "2019-03-26"

> attr(y, "class")

[1] "Date"

> is.object(y)

[1] TRUE


> class(y) = "NonDate"

> y

[1] 17981

attr(,"class")

[1] "NonDate"

> is(y, "Date")

[1] FALSE

> inherits(y, "Date")

[1] FALSE



> And two, because class definitions can exist outside the code itself.
> e.g. As part of an object oriented model.
> (Being "Object Oriented" is just as much about models as it is about
> syntax).
>

Again, I suppose? In fact they have to S3, as I just pointed out above.
But, I really don't see how this is relevant. I doesn't matter what you
have written down on paper, or in documentation, or in your head as a model
about how your S3 classes relate to eachother, because the S3 dispatch
machinery can't see into any of those places. It can only go by what you
put in the class vector, and that is all it is going to go by (for objects
with a class attribute, ie for any "classes" your code defines)


>
> Furthermore, when you change the class of a vector, list or function, much
> of the original object's structure and behavior remains.
>

Conjecture here, since I don't know what exact behaviors you're referring
to, but if they are ostensibly S3 based (ie they are invoved via an S3
generic), its probably becuse they hit *.default methods which call down to
code internal C which operates based on SEXP type, ie they "escape S3
dispatch" in a sense.


> So, it has "Inherited" or "Extended", in my opinion.
>
Resulting in a class hierarchy.
>

You're welcome to have that opinion, but simply put that is now how
inheritance *for the purposes of S3 dispatch* is defined in S3.


>
> > Dispatch is ONLY done on the class vector for S3 (AFAIK)
>

> Incorrect.
> We've already mentioned the example of head.function().
> In general, this dispatch occurs without the presence of a class attribute.
>

I said the class vector, as in what is returned by class().


 > class(rnorm)

[1] "function"

> attr(rnorm, "class")

NULL


I apologize for not more carefully delineating those two things.

That IS consistent with head hitting head.function for a function but
head.default for a "classed function", because according to the definition
of how S3 behaves, your "classed function" doesn't have any implicit
classes, and you didn't declare that it inherited from the function class.

 The portion that is incorrect with what I said is that if the data object
is NOT an R object, (ie it is an unclassed atomic vector or other built in
type - is.object() returns FALSE ) it MAY have an implicit class which will
affect S3 dispatch. But your "f" classed object does not.


> You seem to be approaching the S3 "class"/dispatch system as something
> that it is not: a formal class system
>
> If I can diverge...
>
> Where did this term "Formal Class System" come from?
> I've never seen it used anywhere else.
> Is is specific to R?
>

No. I'm not using it as a term of art at all, really. I'm using it to mean
explicit, non-implicit, that classes declare formal required internal
structures, etc.

Best,
~G

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

hadley wickham
In reply to this post by Abby Spurdle
I would recommend reading https://adv-r.hadley.nz/base-types.html and
https://adv-r.hadley.nz/s3.html. Understanding the distinction between
base types and S3 classes is very important to make this sort of
question precise, and in my experience, you'll find R easier to
understand if you carefully distinguish between them. (And hence you
shouldn't expect is.x(), inherits(, "x") and is(, "x") to always
return the same results)

Also note that many of is.*() functions are not testing for types or
classes, but instead often have more complex semantics. For example,
is.vector() tests for objects with an underlying base vector type that
have no attributes (apart from names). is.numeric() tests for objects
with base type integer or double, and that have the same algebraic
properties as numbers.

Hadley

On Mon, Mar 25, 2019 at 10:28 PM Abs Spurdle <[hidden email]> wrote:

>
> > I have noticed a discrepancy between is.list() and is(x, “list”)
>
> There's a similar problem with inherits().
>
> On R 3.5.3:
>
> > f = function () 1
> > class (f) = "f"
>
> > is.function (f)
> [1] TRUE
> > inherits (f, "function")
> [1] FALSE
>
> I didn't check what happens with:
> > class (f) = c ("f", "function")
>
> However, they should have the same result, regardless.
>
> > Is this discrepancy intentional?
>
> I hope not.
>
>         [[alternative HTML version deleted]]
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel



--
http://hadley.nz

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Abby Spurdle
In reply to this post by R devel mailing list
> the prison made by ancient design choices

That prison of ancient design choices isn't so bad.

I have no further comments on object oriented semantics.
However, I'm planning to follow the following design pattern.

If I set the class of an object, I will append the new class to the
existing class.

#good
class (object) = c ("something", class (object) )

#bad
class (object) = "something"

I encourage others to do the same.

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Martin Maechler
>>>>> Abs Spurdle
>>>>>     on Thu, 28 Mar 2019 12:26:36 +1300 writes:

    >> the prison made by ancient design choices
    > That prison of ancient design choices isn't so bad.

    > I have no further comments on object oriented semantics.
    > However, I'm planning to follow the following design
    > pattern.

    > If I set the class of an object, I will append the new
    > class to the existing class.

    > #good class (object) = c ("something", class (object) )

#even better ;-)
     
      class(object) <- c("something", class(object))

    > #bad class (object) = "something"

    > I encourage others to do the same.
Indeed.

BUT also tell the thousands of people who do it -- including
somewhat famous R package authors --

*NOT* to use things such as

      if(class(x) == "Date")

or
      switch(class(x),  
             "Date" = ...... ,
      "POSIXct" = ...... ,
             ....
             ...
             stop("invalid class: ", class(x)))

BUT to always use

    inherits(x, "....")

There may be rare exceptions where using   class(x)[1]   is
good, but I have seen many cases where     class(x)[1]  was used
and the R programmers found it smart they knew that
class(x) can be of length more than one, but really their code
would fail *exactly* because good R programmers do *prepend*
their S3 class extension/specialization to the already existing
class.

---

... and then, I do agree with Gabe that (in some cases), using
 formal (aka "S4") classes is really what one should do in order
to get a clean interface.


Martin Maechler
ETH Zurich and R Core Team

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

hadley wickham
In reply to this post by Abby Spurdle
On Wed, Mar 27, 2019 at 6:27 PM Abs Spurdle <[hidden email]> wrote:

>
> > the prison made by ancient design choices
>
> That prison of ancient design choices isn't so bad.
>
> I have no further comments on object oriented semantics.
> However, I'm planning to follow the following design pattern.
>
> If I set the class of an object, I will append the new class to the
> existing class.
>
> #good
> class (object) = c ("something", class (object) )
>
> #bad
> class (object) = "something"
>
> I encourage others to do the same.

I don't think this is a good pattern. It's better to clearly define a
constructor function that checks that `object` is the correct
underlying base type for your class -
https://adv-r.hadley.nz/s3.html#s3-classes.

Hadley

--
http://hadley.nz

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Abby Spurdle
In reply to this post by Gabriel Becker-2
I know I said that I had no further comments on object oriented semantics.
However, I found a contradiction in the R documentation.


Gabriel Becker wrote:
> So, there are implicit classes, but *only when the data object is NOT an
"R object"

In the R Language Definition:
> The R specific function typeof returns the type of an R object.
> Lists have elements, each of which can contain any type of R object
> Symbols refer to R objects.
> Unlike most other R objects, environments are not copied

So, according the the R Language Defintion, all objects in R, are R objects.

However, in the help page for UseMethod(), which you've already mentioned:
> An R object is a data object which has a class attribute (and this can be
tested by is.object).

So, according to this, an object in R, isn't necessarily an R object.

These are contradictory to each other.
And I believe that the R Language Definition is correct.
So, the help page for UseMethod() should be changed to match the language
definition.


Hadley Wickham wrote:
> Understanding the distinction between base types and S3 classes
> is very important to make this sort of question precise

Note that the R Language Definition does not mention either "base types" or
"S3 classes".
So, should I be understanding *your* distinction between them?


Martin Maechler wrote:
> I do agree with Gabe that (in some cases), using
> formal (aka "S4") classes is really what one should do

S4 doesn't always do intuitive things, either.

Try the following example:
> library (Matrix)
> m = Matrix (1:24, 4, 6)

> #expected output
> print (m)

> #not expected output
> print (m, quote=FALSE)

However, I still may consider using S4, especially where I would otherwise
use a named list.

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Discrepancy between is.list() and is(x, "list")

Gabriel Becker-2
Abs,

Inline.

On Thu, Mar 28, 2019 at 8:10 PM Abs Spurdle <[hidden email]> wrote:

> I know I said that I had no further comments on object oriented semantics.
> However, I found a contradiction in the R documentation.
>
>
> Gabriel Becker wrote:
> > So, there are implicit classes, but *only when the data object is NOT an
> "R object"
>
> In the R Language Definition:
> > The R specific function typeof returns the type of an R object.
> > Lists have elements, each of which can contain any type of R object
> > Symbols refer to R objects.
> > Unlike most other R objects, environments are not copied
>
> So, according the the R Language Defintion, all objects in R, are R
> objects.
>
> However, in the help page for UseMethod(), which you've already mentioned:
> > An R object is a data object which has a class attribute (and this can
> be tested by is.object).
>
> So, according to this, an object in R, isn't necessarily an R object.
>
> These are contradictory to each other.
> And I believe that the R Language Definition is correct.
> So, the help page for UseMethod() should be changed to match the language
> definition.
>

This could be changed, but it seems largely semantic. Its clear from the
UseMethod documentation that it has a specific definition of "R Object",
that it explicitly defines, that it will use throughout that piece of
documentation. Unfortunate, perhaps, but clearly scoped and unambiguous, in
my opinion.

Also, note that if this was changed, its the documentation that would be
changed, to simply use, for example "classed object" instead of "R Object".
The behavior would be identical and would not change so that all "R
objects" (in the language definition sense) would be treated the same by S3
dispatch.


>
> Hadley Wickham wrote:
> > Understanding the distinction between base types and S3 classes
> > is very important to make this sort of question precise
>
> Note that the R Language Definition does not mention either "base types"
> or "S3 classes".
> So, should I be understanding *your* distinction between them?
>

They are not Hadley's distinction, or mine. We (he in more detail and
covering the corner cases like internal and group generics better) are
describing to you how the system actually works, and why you got the
results which so surprised you.

I think we've done so at this point however, and your phrasing makes it
seem like you're looking for an argument, which I (and I suspect others on
this list) have no interest in, rather than to learn, which I was happy to
try to help you with, so with respect I'll not be engaging you more on this
topic.


>
> Martin Maechler wrote:
> > I do agree with Gabe that (in some cases), using
> > formal (aka "S4") classes is really what one should do
>
> S4 doesn't always do intuitive things, either.
>
> Try the following example:
> > library (Matrix)
> > m = Matrix (1:24, 4, 6)
>
> > #expected output
> > print (m)
>
> > #not expected output
> > print (m, quote=FALSE)
>

So the correct way to print S4 objects is with show(), not print. A
cursory, non-comprehensive look at print.c (do_defaultprint) suggests to me
that print (or rather the c code called by print.default) handles S4
objects when the print call can essentially be transformed in-place to a
show call and then evaluated. That is the case where no additional
arguments are passed to print:

  if (*noParam*s && IS_S4_OBJECT(x) && isMethodsDispatchOn())
PrintObject(x, &data);
 else
PrintValueRec(x, &data);


So it has nothing to do with what argument is used, or the quote argument
in particular, at all:

>* print(x, quote = TRUE)*

<S4 Type Object>

attr(,"x")

 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

attr(,"Dim")

[1] 4 6

attr(,"Dimnames")

attr(,"Dimnames")[[1]]

NULL


attr(,"Dimnames")[[2]]

NULL


attr(,"factors")

list()

attr(,"class")

[1] "dgeMatrix"

attr(,"class")attr(,"package")

[1] "Matrix"


> *print(x, quote = FALSE)*

<S4 Type Object>

attr(,"x")

 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

attr(,"Dim")

[1] 4 6

attr(,"Dimnames")

attr(,"Dimnames")[[1]]

NULL


attr(,"Dimnames")[[2]]

NULL


attr(,"factors")

list()

attr(,"class")

[1] dgeMatrix

attr(,"class")attr(,"package")

[1] Matrix


> *print(x, digits = 5)*

<S4 Type Object>

attr(,"x")

 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

attr(,"Dim")

[1] 4 6

attr(,"Dimnames")

attr(,"Dimnames")[[1]]

NULL


attr(,"Dimnames")[[2]]

NULL


attr(,"factors")

list()

attr(,"class")

[1] "dgeMatrix"

attr(,"class")attr(,"package")

[1] "Matrix"


But basically, don't call print on S4 objects, call show, and everything
should work fine.

Best,
~G




> However, I still may consider using S4, especially where I would otherwise
> use a named list.
>
>
>

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel