RConsortium / S7

S7: a new OO system for R
https://rconsortium.github.io/S7
Other
395 stars 33 forks source link

Should R7 offer encapsulated OO? #202

Open hadley opened 2 years ago

hadley commented 2 years ago

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:

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:

mikmart commented 1 year ago

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 commented 1 year ago

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 commented 9 months ago

So, what happened to this idea? I'm coming from other languages and this "functional oop" seems bizarre to me. I was looking into converting my 7 yo R code because I couldn't make heads and tails out of it. Frankly, I don't know if I'll be gaining much by rewriting if the methods and the class are separate and all over the place. The whole point of OOP, at least to me, is intuitiveness, readability and maintainability. None of that seems to apply to S7. You already have constructor; why not have methods defined in the class as well, instead of requiring the user go through the hoops of generic() and the method(), etc, and then invoke it like a stand-alone function? x.up() is much more readable than up(x), and I'll have easier time maintaining up() if it is encapsulated within x's class.

hadley commented 9 months ago

@tkinsf this is not the place to argue about functional vs encapsulated OO, but I'd strongly encourage you to learn more about functional OOP because IMO it's a great match for the types of problems you tend to solve in data science. One place to start might be https://adv-r.hadley.nz/oo-tradeoffs.html.

tkinsf commented 9 months ago

I take that this idea was shelved then? Thank you, I'll move on.

On Thu, Jan 11, 2024 at 5:27 AM Hadley Wickham @.***> wrote:

@tkinsf https://github.com/tkinsf this is not the place to argue about functional vs encapsulated OO, but I'd strongly encourage you to learn more about functional OOP because IMO it's a great match for the types of problems you tend to solve in data science. One place to start might be https://adv-r.hadley.nz/oo-tradeoffs.html.

— Reply to this email directly, view it on GitHub https://github.com/RConsortium/S7/issues/202#issuecomment-1887163803, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADU7KY6MCKFEKEBPE3B2KTDYN7SFZAVCNFSM5QEZGVA2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBYG4YTMMZYGAZQ . You are receiving this because you were mentioned.Message ID: @.***>