-
Notifications
You must be signed in to change notification settings - Fork 42
Description
It turns out to be trivial (#191) to add encapsulated methods to R7 (i.e. methods that belong to the class/object):
foo <- new_class("foo",
properties = list(
x = "numeric",
y = "numeric"
),
methods = list(
bar = function(self) {
print("Hi!")
},
baz = function(self) {
self@bar()
self@x + self@y
},
change = function(self) {
self@x <- self@x + 1
self
}
),
)
x <- foo(x = 1, y = 2)
x@baz()The current trivial implementation:
-
Handles within package inheritance of methods. Cross-package inheritance needs a little more work to ensure that we don't take a dependency at build-time, rather than load-time (so that environments of the methods match the currently installed package). Would need to adjust
super()implementation to support method access. -
Doesn't have particularly good performance. Prformance will improve a little with a base C implementation of
@, but would require byte code compiler transformation for high speed.
Currently mutability "just works" if you inherit from environment:
foo2 <- new_class("foo", "environment",
properties = list(
x = "numeric"
),
methods = list(
up = function(self) {
self@x <- self@x + 1
invisible(self)
}
)
)
x <- foo2()
x@x <- 1
x@up()
x@xBut a full implementation would need careful consideration of how validate() should work to ensure that objects aren't left in an invalid state.
Overall, I'm pretty neutral on this proposal. There are some big upsides, but also some big downsides. Here are a few of the pros/cons that I've considered so far:
- It's often handy to encapsulate code that only operates on objects of a certain type in to the class, rather than leaving as a free floating function (which typically needs some prefix to avoid clashes with other function names).
- From the outside, encapsulated methods behave similarly to dynamic properties, which we already allow.
- Encapsulated OO allows R7 to also offer an equivalent to R6 (although the performance won't be as good). For better or worse, folks coming from other languages usually find this style of OO more familiar.
- Most modern language use special syntax for in-place mutation. In the code above there's no way to know that
x@up()is actually modifyingxin place: this is potentially extremely confusing. - Would need to think about encapsulation of methods. Maybe a convention that internal methods start with
.would be sufficient? Since they wouldn't be documented and wouldn't be suggested by auto-complete, it would be unlikely for uses to accidentally use them. - It's confusing to provide two different styles of OO in one system. How would developers know which to use when?