Supplementing my comment rather providing a new answer to the question, here's a solution where we still match arguments by position (because we specify additional representation for class B):
.A <- setClass("A", representation(a="character"))
.B <- setClass("B", representation(b="numeric"),
prototype(a="hello"),
contains="A")
.A()
and .B()
replace calls to new("A")
and new("B")
. At some level this is syntactic sugar, but can make object construction more transparent
## construct an object using B's prototype, like new("B", b=1:3)
> .B(b=1:3)
An object of class "B"
Slot "b":
[1] 1 2 3
Slot "a":
[1] "hello"
## construct an object using A's prototype, like new("B", new("A"), b=1:3)
> .B(.A(), b=1:3)
An object of class "B"
Slot "b":
[1] 1 2 3
Slot "a":
character(0)
(the second example uses the fact that unnamed arguments to new
or B
are used to initialize inherited classes).
It isn't so friendly for the user to have to use .A
or .B
directly, e.g., because the signature is just ...
and so would be documented as 'see the definition of slots for class A'. This disrupts the separation of interface and implementation that is a strength of OOP. Also, one or the other behavior in the last code chunk (.B(.A(a=a), b=b)
or .B(a=a, b=b)
) might not be the intention. So instead provide a function that is exposed to the user, perhaps doing some initial data massage
A <- function(a=character(), ...) {
## nothing special, just a public constructor
.A(a=a, ...)
}
B <- function(b, a="hello", ...) {
a <- tolower(a) ## no SHOUTing!
.B(A(a=a), b=b) ## A's public interface; no need for B to know A's details
}
The functions A and B define the interface, provide the user with hints about what acceptable arguments are without tying the constructor to the class definition, and perform preliminary data massaging. The latter can make initialize
methods unnecessary, which is a good thing because these have a complicated contract (they're supposed to initialize and be copy constructors, and as we've seen above unnamed arguments are supposed to initialize base classes) that most people get wrong.
Mostly these are just my opinions.