r-lib / R6

Encapsulated object-oriented programming for R
https://R6.r-lib.org
Other
403 stars 56 forks source link

Diamond inheritance pattern and interfaces #163

Open romainfrancois opened 5 years ago

romainfrancois commented 5 years ago

Follow up to #9 with perhaps a different spin, and additional context here https://github.com/apache/arrow/pull/2714#discussion_r224381298

When doing R6 promotion of class hierarchies like these: https://arrow.apache.org/docs/cpp/classarrow_1_1io_1_1_random_access_file.html

image

a concept of interfaces à la Java would be useful, perhaps something like the pseudo code:

Seekable <- R6Interface("Seekable", 
  public = list( 
     Seek = function(position){} 
  )
)

RandomAccessFile <- R6Class("RandomAccessFile", 
  inherit = InputStream, 
  implements = list(Seekable), 
  ...
)
gaborcsardi commented 5 years ago

R being a dynamic language, we could also make this more of a convention instead of introducing another "keyword". E.g. just simply:

RandomAccessFile <- R6Class("RandomAccessFile", 
  inherit = list(InputStream, Seekable),
  ...
)

and we would require that for each inherited method at most one is implemented, and the others are NULL, or a special NULL function.

It might be worth checking how other dynamic languages are doing this.

gaborcsardi commented 5 years ago

The Python way seems too difficult for me, and it is also apparently different in Python 2 and 3.

romainfrancois commented 5 years ago

IIRC the issue with having them all in inherit was with super$

but yeah I guess the convention could be that super$ means the first and then the other are accessed with something different, e.g. self$interfaces$Seekable$Seek() or something.

gaborcsardi commented 5 years ago

That's why we would allow only one implementation. This is the one that will be called via super. The other instances of the same method (from other parent classes) must be NULL.

But maybe the implements keyword is better, it is surely more explicit.

gaborcsardi commented 5 years ago

But then with implements we need to take care about inheriting interfaces, etc.

gaborcsardi commented 5 years ago

But then if we have new syntax, we might as well do the original suggestion in #9. Maybe.

berndbischl commented 5 years ago

+1 for the suggestion to have interfaces in R6. it is the feature i am missing the most currently, to ensure good design in my class systems

berndbischl commented 5 years ago

i actually nearly started writing something simple here myself

chrisknoll commented 2 years ago

I'd like to echo support for implementing (no pun intended) some sort of interface mechanism. I think the Java (and probably others) thought through he issues of multiple-inheritance when they landed on 'single inheritance, multiple interfaces'....

So, the idea of introducing the notion of an 'interface declaration' (separate from R6Class, it would be something like R6Interface), and allow the R6Class() to specify an 'implements' list that specifies a set of R6Interface types that will be checked for implementation after the class is defined. Then super$ always knows it's referencing the single inherited type, while the implements specification can specify multiple interfaces that will result in a validation check on the class definition that has the proper methods defined (which also will find implemented methods in the inheritance hierarchy).

This would be really wonderful to introduce, and would really be worth any additional size to the library to support it. This isn't sugar syntax, this is a real gap in functionality.