Unexpected behavior with inheritance and using S3 classes as slots in S4 class

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

Unexpected behavior with inheritance and using S3 classes as slots in S4 class

Ezra Tucker-2
Hi all,

I have a situation where I'm trying to use S3 classes as slots in an S4 class and am running into some problems with inheritance.

My example:
## with S3 classes
a <- function(x) structure(x, class = "a")
b <- function(x) structure(x, class = c("b", "a"))
setOldClass(c("a", "b"))

a_1 <- a("hello")
b_1 <- b("world")
n <- setClass("n", slots = c(s1 = "a"))
n(s1 = a_1) # works as expected
is(b_1, "a") # yields TRUE
n(s1 = b_1) # doesn't work-- unexpected

Error message is:
Error in validObject(.Object) :
invalid class “n” object: invalid object for slot "s1" in class "n": got class "b", should be or extend class "a"

Digging in, validObject looks like it's calling getClassDef, which gives me
> getClassDef("b")
Virtual Class "b" [in ".GlobalEnv"]

Slots:

Name: .S3Class
Class: character

Extends: "oldClass"

-- it extends "oldClass" but it doesn't extend class "a" despite the fact the S3 version does.

Now, In this simple example, I could have defined a and b as s4 classes, but in the real world, I didn't define them and have to use setOldClass.
I'm currently getting around this issue by doing setClassUnion("ab", c("a", "b")) and then specify to use "ab" as the type for the slot, but since I already know b inherits from a, this ought not to be necessary (and there are other reasons this is undesirable).

1. Is there anything I missed, in setting up this simple example, that would cause n(s1 = b_1) to work properly? Or is there anything I should do other than setClassUnion?
2. Is this a shortcoming of validObject or getClassDef?

Thank you all!
-Ezra
        [[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: Unexpected behavior with inheritance and using S3 classes as slots in S4 class

Simon Urbanek
Ezra,

I think it's just the fact the you specified the wrong class inheritance in setOldClass() - it has to match you S3 definition, so it should be:

setOldClass(c("b","a"))

In which case it works:

> n(s1 = b_1)
An object of class "n"
Slot "s1":
[1] "world"
attr(,"class")
[1] "b" "a"

Cheers,
Simon



> On Jan 28, 2021, at 15:55, Ezra Tucker <[hidden email]> wrote:
>
> Hi all,
>
> I have a situation where I'm trying to use S3 classes as slots in an S4 class and am running into some problems with inheritance.
>
> My example:
> ## with S3 classes
> a <- function(x) structure(x, class = "a")
> b <- function(x) structure(x, class = c("b", "a"))
> setOldClass(c("a", "b"))
>
> a_1 <- a("hello")
> b_1 <- b("world")
> n <- setClass("n", slots = c(s1 = "a"))
> n(s1 = a_1) # works as expected
> is(b_1, "a") # yields TRUE
> n(s1 = b_1) # doesn't work-- unexpected
>
> Error message is:
> Error in validObject(.Object) :
> invalid class “n” object: invalid object for slot "s1" in class "n": got class "b", should be or extend class "a"
>
> Digging in, validObject looks like it's calling getClassDef, which gives me
>> getClassDef("b")
> Virtual Class "b" [in ".GlobalEnv"]
>
> Slots:
>
> Name: .S3Class
> Class: character
>
> Extends: "oldClass"
>
> -- it extends "oldClass" but it doesn't extend class "a" despite the fact the S3 version does.
>
> Now, In this simple example, I could have defined a and b as s4 classes, but in the real world, I didn't define them and have to use setOldClass.
> I'm currently getting around this issue by doing setClassUnion("ab", c("a", "b")) and then specify to use "ab" as the type for the slot, but since I already know b inherits from a, this ought not to be necessary (and there are other reasons this is undesirable).
>
> 1. Is there anything I missed, in setting up this simple example, that would cause n(s1 = b_1) to work properly? Or is there anything I should do other than setClassUnion?
> 2. Is this a shortcoming of validObject or getClassDef?
>
> Thank you all!
> -Ezra
> [[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: Unexpected behavior with inheritance and using S3 classes as slots in S4 class

Ezra Tucker-2
Wow - can't believe it was that simple (and obvious now that I see the answer). Thanks Simon!

-Ezra

--
[hidden email]
m: 818-203-0269
LinkedIn: linkedin.com/in/ezztucker
Github: github.com/minimenchmuncher

‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐

On Thursday, January 28th, 2021 at 8:41 AM, Simon Urbanek <[hidden email]> wrote:

> Ezra,
>
> I think it's just the fact the you specified the wrong class inheritance in setOldClass() - it has to match you S3 definition, so it should be:
>
> setOldClass(c("b","a"))
>
> In which case it works:
>
> > n(s1 = b_1)
>
> An object of class "n"
>
> Slot "s1":
>
> [1] "world"
>
> attr(,"class")
>
> [1] "b" "a"
>
> Cheers,
>
> Simon
>
> > On Jan 28, 2021, at 15:55, Ezra Tucker [hidden email] wrote:
> >
> > Hi all,
> >
> > I have a situation where I'm trying to use S3 classes as slots in an S4 class and am running into some problems with inheritance.
> >
> > My example:
> >
> > with S3 classes
> > ---------------
> >
> > a <- function(x) structure(x, class = "a")
> >
> > b <- function(x) structure(x, class = c("b", "a"))
> >
> > setOldClass(c("a", "b"))
> >
> > a_1 <- a("hello")
> >
> > b_1 <- b("world")
> >
> > n <- setClass("n", slots = c(s1 = "a"))
> >
> > n(s1 = a_1) # works as expected
> >
> > is(b_1, "a") # yields TRUE
> >
> > n(s1 = b_1) # doesn't work-- unexpected
> >
> > Error message is:
> >
> > Error in validObject(.Object) :
> >
> > invalid class “n” object: invalid object for slot "s1" in class "n": got class "b", should be or extend class "a"
> >
> > Digging in, validObject looks like it's calling getClassDef, which gives me
> >
> > > getClassDef("b")
> > >
> > > Virtual Class "b" [in ".GlobalEnv"]
> >
> > Slots:
> >
> > Name: .S3Class
> >
> > Class: character
> >
> > Extends: "oldClass"
> >
> > -- it extends "oldClass" but it doesn't extend class "a" despite the fact the S3 version does.
> >
> > Now, In this simple example, I could have defined a and b as s4 classes, but in the real world, I didn't define them and have to use setOldClass.
> >
> > I'm currently getting around this issue by doing setClassUnion("ab", c("a", "b")) and then specify to use "ab" as the type for the slot, but since I already know b inherits from a, this ought not to be necessary (and there are other reasons this is undesirable).
> >
> > 1.  Is there anything I missed, in setting up this simple example, that would cause n(s1 = b_1) to work properly? Or is there anything I should do other than setClassUnion?
> > 2.  Is this a shortcoming of validObject or getClassDef?
> >
> > Thank you all!
> >
> > -Ezra
> >
> > [[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