Home > Back-end >  R Reference Class: Field of type 'units'
R Reference Class: Field of type 'units'

Time:12-15

I have a reference class, one of the fields for which is a 'units' object from the R units package. My initial class definition looked like this:

MyClass = 
    setRefClass(
        "MyClass",
        fields = list(units_value = "numeric")
    )

uv = units::as_units(10, 'cm')

mc = MyClass(units_value = uv)

Which threw the following error:

Error: invalid assignment for reference class field ‘units_value’, should be from class “numeric” or a subclass (was class “units”)

So I tried to set the field class to units like so:

MyClass = 
    setRefClass(
        "MyClass",
        fields = list(units_value = "units")
    )

uv = units::as_units(10, 'cm')

mc = MyClass(units_value = uv)

But this throws a whole new error:

Error in refClassInformation(Class, contains, fields, methods, where) : 
class “units” for field ‘units_value’ is not defined

For the moment I've settled for using a non-typed class constructor, but this seems like such a simple problem that it's hard for me to believe there's no way to construct a Reference Class with a units object as a field. Does anyone know how to accomplish this?

CodePudding user response:

"units" is an S3 class, so doesn't have a formal class definition. To get an S3 class to be represented inside an S4 or reference class, you need to register it first as a formally defined class using setOldClass

We can see that there is no formal class definition for "units" if we do:

library(units)

getClassDef("units")
#> NULL

But if we use setOldClass, we can see it becomes registered:

setOldClass("units")

getClassDef("units")
#> Virtual Class "units" [in ".GlobalEnv"]
#> 
#> Slots:
#>                 
#> Name:   .S3Class
#> Class: character
#> 
#> Extends: "oldClass"

This now allows your second code block to work as expected:

MyClass = 
  setRefClass(
    "MyClass",
    fields = list(units_value = "units")
  )

uv = units::as_units(10, 'cm')

mc = MyClass(units_value = uv)

mc
#> Reference class object of class "MyClass"
#> Field "units_value":
#> 10 [cm]

The alternative is to set the class as "ANY" instead of "units", but this always feels like a last resort if you are trying to stick to OOP principles.

Created on 2022-12-15 with reprex v2.0.2

  • Related