Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should R7 offer encapsulated OO? #202

Open
hadley opened this issue Mar 7, 2022 · 5 comments
Open

Should R7 offer encapsulated OO? #202

hadley opened this issue Mar 7, 2022 · 5 comments

Comments

@hadley
Copy link
Member

hadley commented Mar 7, 2022

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@x

But 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 modifying x in 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?
@mikmart
Copy link

mikmart commented Dec 4, 2022

The discussion in #245 made me wonder if static methods/associated functions would be useful. That would allow "alternative constructors" etc. to live inside the class. No idea if this is feasible, but I was thinking like:

range <- new_class("range",
  properties = list(
    start = class_numeric,
    end = class_numeric
  )
)

range@from <- new_generic("range@from", "x")

method(range@from, class_numeric) <- function(x) {
  range(min(x), max(x))
}

range@from(1:5) == range(1L, 5L)

I notice something like this has been considered based on the note about "class methods that lack self" in #191.

@lawremi
Copy link
Collaborator

lawremi commented Dec 6, 2022

The above idea is interesting and probably deserves its own issue. Since classes are objects, we would probably want some other syntax than @ for accessing static members. Maybe just $.

@tkinsf

This comment was marked as off-topic.

@hadley

This comment was marked as outdated.

@tkinsf

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants