[R] How to create a proper S4 class?

Martin Morgan mtmorg@n@b|oc @end|ng |rom gm@||@com
Thu Nov 18 16:24:03 CET 2021


From my example, 

  as(employees, "People")

more general coercion is possible; see the documentation ?setAs.

From your problem description I would have opted for the solution that you now have, with two slots rather than inheritance. Inheritance has a kind of weird contract when using another package, where you're agreeing to inherit whatever generics and methods are / will be defined on the object, by any package loaded by the user; a usual benefit of object-oriented programming is better control over object type, and this contract seems to total undermine that.

I actually don't know how to navigate the mysterious error; the secret is somewhere in the cbind / cbind2 documentation, but this seems quite opaque to me.

Martin



On 11/17/21, 8:00 PM, "Leonard Mada" <leo.mada using syonic.eu> wrote:

    Dear Martin,


    thank you very much for the guidance.


    Ultimately, I got it running. But, for mysterious reasons, it was 
    challenging:

    - I skipped for now the inheritance (and used 2 explicit non-inherited 
    slots): this is still unresolved; [*]

    - the code is definitely cleaner;


    [*] Mysterious errors, like:

    "Error in cbind(deparse.level, ...) :
       cbind for agentMatrix is only defined for 2 agentMatrices"


    One last question pops up:

    If B inherits from A, how can I down-cast back to A?

    b = new("B", someA);

    ??? as.A(b) ???

    Is there a direct method?

    I could not explore this, as I am still struggling with the inheritance. 
    The information may be useful, though: it helps in deciding the design 
    of the data-structures. [Actually, all base-methods should work natively 
    as well - but to have a solution in any case.]


    Sincerely,


    Leonard


    On 11/17/2021 5:48 PM, Martin Morgan wrote:
    > Hi Leonard --
    >
    > Remember that a class can have 'has a' and 'is a' relationships. For instance, a People class might HAVE slots name and age
    >
    > .People <- setClass(
    >      "People",
    >      slots = c(name = "character", age = "numeric")
    > )
    >
    > while an Employees class might be described as an 'is a' relationship -- all employeeds are people -- while also having slots like years_of_employment and job_title
    >
    > .Employees <- setClass(
    >      "Employees",
    >      contains = "People",
    >      slots = c(years_of_employment = "numeric", job_title = "character")
    > )
    >
    > I've used .People and .Employees to capture the return value of setClass(), and these can be used as constructors
    >
    > people <- .People(
    >     name = c("Simon", "Andre"),
    >     age = c(30, 60)
    > )
    >
    > employees = .Employees(
    >      people, # unnamed arguments are class(es) contained in 'Employees'
    >      years_of_employment = c(3, 30),
    >      job_title = c("hard worker", "manager")
    > )
    >
    > I would not suggest using attributes in addition to slots. Rather, embrace the paradigm and represent attributes as additional slots. In practice it is often helpful to write a constructor function that might transform between formats useful for users to formats useful for programming, and that can be easily documented.
    >
    > Employees <-
    >      function(name, age, years_of_employment, job_title)
    > {
    >      ## implement sanity checks here, or in validity methods
    >      people <- .People(name = name, age = age)
    >      .Employees(people, years_of_employment = years_of_employment, job_title = job_title)
    > }
    >
    > plot() and lines() are both S3 generics, and the rules for S3 generics using S4 objects are described in the help page ?Methods_for_S3. Likely you will want to implement a show() method; show() is an S4 method, so see ?Methods_Details. Typically this should use accessors rather than relying on direct slot access, e.g.,
    >
    > person_names <- function(x) x using name
    > employee_names <- person_names
    >
    > The next method implemented is often the [ (single bracket subset) function; this is relatively complicated to get right, but worth exploring.
    >
    > I hope that gets you a little further along the road.
    >
    > Martin Morgan
    >
    > On 11/16/21, 11:34 PM, "R-help on behalf of Leonard Mada via R-help" <r-help-bounces using r-project.org on behalf of r-help using r-project.org> wrote:
    >
    >      Dear List-Members,
    >
    >
    >      I want to create an S4 class with 2 data slots, as well as a plot and a
    >      line method.
    >
    >
    >      Unfortunately I lack any experience with S4 classes. I have put together
    >      some working code - but I presume that it is not the best way to do it.
    >      The actual code is also available on Github (see below).
    >
    >
    >      1.) S4 class
    >      - should contain 2 data slots:
    >      Slot 1: the agents:
    >        = agentMatrix class (defined externally, NetlogoR S4 class);
    >      Slot 2: the path traveled by the agents:
    >         = a data frame: (x, y, id);
    >        - my current code: defines only the agents ("t");
    >      setClass("agentsWithPath", contains = c(t="agentMatrix"));
    >
    >      1.b.) Attribute with colors specific for each agent
    >      - should be probably an attribute attached to the agentMatrix and not a
    >      proper data slot;
    >      Note:
    >      - it is currently an attribute on the path data.frame, but I will
    >      probably change this once I get the S4 class properly implemented;
    >      - the agentMatrix does NOT store the colors (which are stored in another
    >      class - but it is useful to have this information available with the
    >      agents);
    >
    >      2.) plot & line methods for this class
    >      plot.agentsWithPath;
    >      lines.agentsWithPath;
    >
    >
    >      I somehow got stuck with the S4 class definition. Though it may be a
    >      good opportunity to learn about S4 classes (and it is probably better
    >      suited as an S4 class than polynomials).
    >
    >
    >      The GitHub code draws the agents, but was somehow hacked together. For
    >      anyone interested:
    >
    >      https://github.com/discoleo/R/blob/master/Stat/ABM.Models.Particles.R
    >
    >
    >      Many thanks,
    >
    >
    >      Leonard
    >
    >      ______________________________________________
    >      R-help using r-project.org mailing list -- To UNSUBSCRIBE and more, see
    >      https://stat.ethz.ch/mailman/listinfo/r-help
    >      PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
    >      and provide commented, minimal, self-contained, reproducible code.


More information about the R-help mailing list