Classes vs. prototypes

89 views
Skip to first unread message

Matthew Browne

unread,
Apr 14, 2024, 4:14:01 PMApr 14
to object-co...@googlegroups.com
Hi, this is a question for Cope, but I thought others might be interested as well so that's why I'm posting here...

I just read this paper about the Self programming language (I had read about it before but not this specific paper, which is a bit more philosophical):


Cope, after reading this, it made me wonder if you had thought about prototypes vs. classes when designing the trygve language, and if so why you decided on classes. I suppose at the end of the day, both enable a similar concept: inheritance. Having used Javascript for many years (which is prototype-based), including before the introduction of "classes" of course, I haven't noticed huge practical differences in how inheritance works for most use cases. But then again, the JS object model was only inspired by Self and not exactly like it, so it seems that some of the nice features of Self are absent in JS. Anyway, I would be curious to hear your thoughts on this.

Cheers,
Matt

Raoul Duke

unread,
Apr 14, 2024, 4:42:46 PMApr 14
to object-co...@googlegroups.com
(interesting question. prototypes alway feel weird to me having not grown up on it. at some level i don't really understand how any inheritance can ever be seen as a net positive. maybe barely in-the-small but too easily reaches tipping point of badness. but i'm a pessimist.)

Alexandre Gravem

unread,
Apr 15, 2024, 7:22:53 AMApr 15
to object-composition
Prototypes are closely related with mixins. I very much agree with the author of this paper (http://fare.tunes.org/files/cs/poof.pdf) that they are the very essence of OOP. Maybe because my notion of OOP is more informed by Lisp than Smalltalk.

Raoul Duke

unread,
Apr 17, 2024, 9:31:04 AMApr 17
to object-co...@googlegroups.com
 heh all the mainstream approaches are seemingly empirically doomed to fail at some point for some use cases. funny this came up for the millionth time in the programming subconscious:


 maybe yes smalltalk & dci can be good exceptions to the rule? :-)


--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/object-composition/8ebcb5cc-1282-4ad1-8b73-4fa51236cca8n%40googlegroups.com.

Matthew Browne

unread,
Apr 17, 2024, 4:10:18 PMApr 17
to object-co...@googlegroups.com
BTW, there is no inheritance in Go (i.e. neither classes nor prototypes) - only forwarding:

So it's possible to do without it, but I think maybe prototypes or classes with inheritance are fine for simple data objects, since most of the complexity is in the behavior - which can be handled with DCI contexts. I was mostly wondering why if the goal is a simple kind of inheritance for data objects, what are the tradeoffs between classes and prototypes? I think the paper I linked to makes a good case for prototypes handling some use cases much better than classes, while still being able to accomplish what classes can do.


James O Coplien

unread,
Apr 18, 2024, 2:44:23 AMApr 18
to noreply-spamdigest via object-composition
I’ve had prototypes and similar things in my OO toolbox from the very beginning back in the 1980s. In my C++ book I called them exemplars after one variant (I think based in Smalltalk) that was being discussed at the time. And I’ve always been an advocate of Ungar and Smith’s work. I went out of my way to invite Ungar to the 1996 OOPSLA PC because I thought his perspective was getting neglected in the industry. We’re still in touch; he just retired from Sun.

DCI by its nature has much of the feel of exemplars in terms of type dynamics. One big deal about self is its use of delegation to do what inheritance does.  First, it doesn’t offer much cognitive leverage beyond what one finds in inclusion polymorphism — in essence, that’s what the famous Treaty of Orlando was about. So it really doesn't pump up the type system. It may make things like mix-ins possible but, well, trygve already has just about the same capability. I haven’t looked at the self type system in detail to see what it provides in the way of static type support (or signature support) but I always felt that Smalltalk was lacking in this area and preferred C++ which admittedly overdid it, but I think Smalltalk underdid it. The self language is likely the same, at best.

Many of the other benefits mentioned in the paper are kind of cute hobby horses that are a long, long way from solving The Problems of Software Engineering. Singletons is one example.

One way of thinking about self is that is just that it has a slightly more naked MOP than Smalltalk, both of which are quite a bit more naked than Java, which is more naked (but in a very strange and confusing way) than C++. I think that more MOP-intensive approaches — as we find in the great failed experiment called AOP — are distinct from what we’re trying to be able to express with DCI. To oversimplify, MOPs are an engineering tool whereas DCI is a thinking tool.

Prototypes can be a thinking tool, too, but I don’t think that paradigm of objects is in any way fundamentally different from what we express in DCI. So it’s wise to err on the side of familiarity, all other things being equal.

Another big deal about self is how it gets high performance with novel uses of snap-links and other quasi-cacheing strategies that are pretty damned brilliant. Most of those found their way back into Smalltalk. Again, those are engineering strategies rather than paradigms.

James O Coplien

unread,
Apr 18, 2024, 2:44:26 AMApr 18
to noreply-spamdigest via object-composition
I’ve had prototypes and similar things in my OO toolbox from the very beginning back in the 1980s. In my C++ book I called them exemplars after one variant (I think based in Smalltalk) that was being discussed at the time. And I’ve always been an advocate of Ungar and Smith’s work. I went out of my way to invite Ungar to the 1996 OOPSLA PC because I thought his perspective was getting neglected in the industry. We’re still in touch; he just retired from Sun.

DCI by its nature has much of the feel of exemplars in terms of type dynamics. One big deal about self is its use of delegation to do what inheritance does.  First, it doesn’t offer much cognitive leverage beyond what one finds in inclusion polymorphism — in essence, that’s what the famous Treaty of Orlando was about. So it really doesn't pump up the type system. It may make things like mix-ins possible but, well, trygve already has just about the same capability. I haven’t looked at the self type system in detail to see what it provides in the way of static type support (or signature support) but I always felt that Smalltalk was lacking in this area and preferred C++ which admittedly overdid it, but I think Smalltalk underdid it. The self language is likely the same, at best.

Many of the other benefits mentioned in the paper are kind of cute hobby horses that are a long, long way from solving The Problems of Software Engineering. Singletons is one example.

One way of thinking about self is that is just that it has a slightly more naked MOP than Smalltalk, both of which are quite a bit more naked than Java, which is more naked (but in a very strange and confusing way) than C++. I think that more MOP-intensive approaches — as we find in the great failed experiment called AOP — are distinct from what we’re trying to be able to express with DCI. To oversimplify, MOPs are an engineering tool whereas DCI is a thinking tool.

Prototypes can be a thinking tool, too, but I don’t think that paradigm of objects is in any way fundamentally different from what we express in DCI. So it’s wise to err on the side of familiarity, all other things being equal.

Another big deal about self is how it gets high performance with novel uses of snap-links and other quasi-cacheing strategies that are pretty damned brilliant. Most of those found their way back into Smalltalk. Again, those are engineering strategies rather than paradigms.
On 16 Apr 2024, at 01.53, Raoul Duke <rao...@gmail.com> wrote:

Matthew Browne

unread,
Apr 21, 2024, 6:02:06 PMApr 21
to object-co...@googlegroups.com

Thanks Cope, I think the familiarity argument makes a lot of sense. I'd like to pivot now to talking about this problem in general and not only as it applies to the trygve language. (I can see how implementing prototypes, even if we all agreed they were better than classes in some way, could be a bit of a distraction from the main point.)

Like you, I don't think having to declare a singleton for a class is a big deal. To me, the most compelling argument for prototypes (based on my own programming experience) is the ability to use traits/mixins that can reflect the changes to an object's data needs over time. Promoters of prototype-based languages or languages with traits have long talked about the benefits of using traits to define behavior that can be added to different types of objects as needed, but I think those who have used DCI realize that this sort of free-for-all is far less desirable than DCI contexts.

However, I think there's still such a thing as a "data trait," and I don't think classic DCI fully solves that problem. As an example, I think it's pretty common to store some common properties that apply to all users (or even all people—since the system may also need to represent people who never log into the system)—name, address, email, password, etc—and also store data related to the user's role, or multiple roles at the same time. Roles like Administrator, Instructor, Parent, Student, Customer, etc.

I have been slowly reading Meilir Page-Jones's book Fundamentals of Object-Oriented Design in UML (originally titled "What every programmer should know about object-oriented design"). I'm mainly reading it for historical context, to understand classic OOP thinking—both its best parts and its faults. It includes an interesting example of a fictional dog-tracking application that needs to record which person owns which dogs, and also how many dogs each person owns. This is a simple example of what I'm talking about: a person can become a dog owner at any time, and they could also cease to be a dog owner, and it would be best not to have to throw out the Person object or change its identity just for that reason. The relevant data property here is numOfDogsOwned. Based on the example in the book, if we were to pretend that every person is always a dog owner, it would look something like this if represented as a class:

class DogOwner {
  name: PersonalName
  address: StreetAddress
  numOfDogsOwned: Integer
}

The author points out a flaw in all the OOP languages that were popular at the time: "They don't support an object's ability to acquire or lose class membership or to hold multiple class membership (apart from that implied by the inheritance hierarchy) at one time. A design approach that provides such abilities [meaning the class and inheritance-based designs proposed in the book] is a work-around for an object-oriented language flaw."

Prototype-based languages don't suffer from this flaw. And if I understand it correctly, the Self language goes a step further by allowing an object to have more than one parent slot that automatically delegates to a prototype, meaning you can have not only multiple inheritance of data properties but also dynamic multiple inheritance of such properties (i.e., add and remove the prototypes you need at run-time as needed). I'm not claiming it's a silver bullet, but it certainly seems like a nice feature to have.

Raoul Duke

unread,
Apr 21, 2024, 6:32:56 PMApr 21
to object-co...@googlegroups.com
i feel like for me the most important distinguishing thing is the use case vis-à-vis the expression problem... fairly often i'd rather have a functional-relational paradigm. but if you truly have a fixed set of operations and know you will want to add new types that support those, object orientation can work wonders. 

Raoul Duke

unread,
Apr 21, 2024, 6:34:28 PMApr 21
to object-co...@googlegroups.com
dynamism of any kind is very very dangerous deal with the devil in my mind wrt things like maintenance and robust extensibility. 

Matthew Browne

unread,
Apr 23, 2024, 7:43:37 AMApr 23
to object-co...@googlegroups.com
On 4/21/24 6:34 PM, Raoul Duke wrote:
dynamism of any kind is very very dangerous deal with the devil in my mind wrt things like maintenance and robust extensibility.

I get your point, but I think that any move from class-oriented to object-oriented inherently means more dynamism—but it doesn't have to mean completely giving up type safety. And this is all in the interest of being more faithful to our mental models and the fact that the real world (and real systems) aren't static. I think DCI accomplishes this quite well with role-object contracts. And even if you have dynamic mixins for your data objects, those contracts would ensure that at least you would be failing early if there were an incorrect typecast or something prior to when you pass it in to a DCI context.

Another factor to consider: in the case of data objects, removing the trait when it's no longer needed is arguably less important than it is for DCI roles. (In the case of DCI roles, the chance of name conflicts or just confusion if the role methods stick around longer than they should is probably higher.) So if we only really care about being able to add data traits to a new or existing instance, the problem becomes a bit simpler and some existing statically-typed languages (i.e. the ones with traits features) aren't so far off from this.

Maybe it would even be preferable not to ever remove data traits: in the dog-owner example, it doesn't matter if you delete the numDogsOwned property when it's no longer needed (assuming it was already stored in a database somewhere if it needs to be archived) because it would always be zero at that point. But I'm guessing there are other cases where deleting properties probably wouldn't be the best option.

James O Coplien

unread,
Apr 23, 2024, 12:08:38 PMApr 23
to noreply-spamdigest via object-composition


On 23 Apr 2024, at 14.43, Matthew Browne <mbro...@gmail.com> wrote:

I get your point, but I think that any move from class-oriented to object-oriented inherently means more dynamism—but it doesn't have to mean completely giving up type safety. And this is all in the interest of being more faithful to our mental models and the fact that the real world (and real systems) aren't static. I think DCI accomplishes this quite well with role-object contracts.

Not if the contracts are only conventions.

If an object becomes a Role-player, when does one check whether the signatures line up? It might be instructive to compare Trygve’s Smalltalk implementation with the implementations in Ruby, Python, and Javascript, and also with those that Rune is doing in C# and with the ones in trygve. There is a range of possible protections ranging from enforcing signature matching at source time (“compile time”) to ensure that the object bound to the Role can satisfy its signature. Is it fundamental that such protection comes with limitations? And which of our implementations in fact do not check at compile time but give the moral equivalent of message-not-understood at run time? That’s 100% type safe; it’s just that the timing is inconvenient...

Anyone want to take a shot at tabulating this? I don’t know enough about the various dialects to be able to do it myself.

Raoul Duke

unread,
Apr 23, 2024, 12:11:38 PMApr 23
to object-co...@googlegroups.com
the irony being that some strongish static type systems are not very good at handling the concept of "self" in DCI meaning and use? those languages are painted into a corner a tad. 

James O Coplien

unread,
Apr 23, 2024, 12:16:39 PMApr 23
to noreply-spamdigest via object-composition


On 22 Apr 2024, at 01.02, Matthew Browne <mbro...@gmail.com> wrote:

The relevant data property here is numOfDogsOwned. Based on the example in the book, if we were to pretend that every person is always a dog owner, it would look something like this if represented as a class:

class DogOwner {
  name: PersonalName
  address: StreetAddress
  numOfDogsOwned: Integer
}

To me there is a fundamental flaw in reasoning here in that numOfDogsOwned is a data object or that it represents a primitive value. To me it is an iteration over a database counting items with given properties and/or relationships. It’s like a balance in a bank account: it’s not data, but rather a value produced by a computation.

Eiffel and other languages provide one paradigm for dealing with this in other differentiating between eliciting a data value and the value produced by a function evaluation. The trygve language can also kind of do this in a weak sense.

Further, the design that Matt talks about has a potential data synchronization bug because it unnecessarily tracks information redundantly (both the identities of dogs owned by a given owner as well as a tally of how many there are). Such tripe should be relegated to local optimizations rather than glorified as an issue at the level of full language or paradigm support.

Raoul Duke

unread,
Apr 23, 2024, 12:21:02 PMApr 23
to object-co...@googlegroups.com
"duals" can lead unfortunately to exit wounds. 

Matthew Browne

unread,
Apr 23, 2024, 3:58:48 PMApr 23
to object-co...@googlegroups.com
On 4/23/24 12:16 PM, James O Coplien wrote:
To me there is a fundamental flaw in reasoning here in that numOfDogsOwned is a data object or that it represents a primitive value. To me it is an iteration over a database counting items with given properties and/or relationships. It’s like a balance in a bank account: it’s not data, but rather a value produced by a computation.

These are all valid points, but I think the author just intended it to be a simple example for a simple fictional system—probably unrealistically simple.

So let's use a different example: suppose we need to keep track of a university student's level (first year of college, second year, etc; this property could alternatively be named year if that's not too confusing since it's not the calendar year). Additional requirements: the Student type needs to inherit from a base User type, and we want Student to be a trait rather than a class, because of situations like this: perhaps someone is already in the system as a Teacher but then they decide to enroll in a class so they become a Student as well.

I think we now have a technical problem to solve that's very similar to what I described for the dog-owner example, which isn't merely a result of a flawed design / mental model.

Matthew Browne

unread,
Apr 23, 2024, 4:32:50 PMApr 23
to object-co...@googlegroups.com
On 4/23/24 12:08 PM, James O Coplien wrote:
>
> Anyone want to take a shot at tabulating this? I don’t know enough
> about the various dialects to be able to do it myself.

I can share what I know at least...

I'm pretty sure none of the dynamic language implementations we have
include compile-time type checking for role-object contracts—the
Javascript implementations on fulloo.info definitely don't. But
compile-time checks would be possible in TypeScript (unfortunately the
only way would be to fork it though).

I'm not best-suited to answer questions about C++ or C#.

I'm not sure about HaxeDCI and ScalaDCI since they are implemented using
macros.

Rust is an interesting language when it comes to this... there's an
existing DCI example here, but it only allows for instances of the
'Account' class specifically to be used as role players in the
transfer_money context:
https://github.com/atherlangga/dci-sample/blob/master/transfer_money/rust/transfer_money.rs

I suspect that something more flexible would be possible in Rust, but it
might end up falling under your "do not check at compile time but give
the moral equivalent of message-not-understood at run time" category.

Matthew Browne

unread,
Apr 24, 2024, 7:30:39 AMApr 24
to object-co...@googlegroups.com
Marvin also has role-object contracts that I assume are enforced at compile time:


I didn't think it had them earlier because I was looking on fulloo.info, and the Marvin examples there don't have any role-object contracts (I'm guessing the website has older versions of the examples).
Reply all
Reply to author
Forward
0 new messages