On Fri, Dec 2, 2022 at 1:33 AM Dmitry Gutov wrote: > > So, what you are asking for is perfectly reasonable in theory. Also the > current theory of OOP suggests that type inheritance comes with its > downsides as well. For instance, the straightforward way Joao was > suggesting (create a defclass) will mean nailing down the internal > structure of a type which would make it more difficult to create > internal changes to it -- because somebody might have inherited from it, > and might be using the exposed fields, etc. > I'm sorry, but it is imperative that this misconception be dispelled. Just by doing: (defclass project-foo-project () ((impl-detail :initform 42)) One is in no way exposing that implementation detail to the subclassing user (if one is concerned the user uses slot-value to access impl-detail's value, one may name it with the '--' double-dash convention or even make it a keyword, but I would say that's completely unnecessary here) If one creates a slot with an reader: (defclass project-foo() ((some-implementation-detail-slot :initform 42) (root :initarg :root :reader project-root))) then one _does_ expose an interface, in that one is adding a method to the generic function project-root, which is exactly what is desired. Likewise for :writer and :accessor. So, long after someone has inherited from project-foo one may as well erase 'root' and represent the root of a foo project in Morse code or anything like that just by reimplementing the project-root method for project-foo. Thus, it's false that using defclass means "nailing down the internal structure of a type". It's just not true for CLOS (or any other OO system I know, for that matter). As Stefan highlighted earlier, the generic functions are the protocol. Offering classes and inheritance doesn't change that: it only makes them more powerful and simpler to instantiate. And if one wants to add some pre-, post- or around- processing to the `project-root` method created by defclass, one need only use :before, :after or :around specifiers. In fact, even if one wanted to prevent instancing of a specific class (I don't think we should here, but let's say project-foo is abstract or a singleton) defclass is still the way to go. One can use a specialization of make-instance or a add a method to initialize-instance for that. In fact, the "nailing down" problem is precisely what the current implementation promotes. The strange technique (transient . "some/path") used here doesn't actively prevent any user from instantiating it because 'cons' and 'transient' are both perfectly accessible symbols. Users will likely use them to work around the missing constructor. And once that happens, _that's_ when `project.el` is stuck with that internal implementation. But if one uses defclass instead, one can retain this freedom and even control or monitor the creation of objects with make-instance and associated CLOS protocols (initialize-instance, reinitialize-instance, etc). Have a look at how eglot-lsp-server class hierarchy to see some of these concepts in action. For more examples, I recommend any good CLOS book. Finally, I know eieio.el isnt CLOS: it doesn't perfectly replicate CLOS unfortunately, but it's decently close for these basic use cases in my personal experience. João