3 ms·
I think, even in prototype systems, the class-vs-instance dichotomy still exists at a conceptual level, even if the language/runtime doesn't make it explicit. M
by skissane 4y ago
I think, even in prototype systems, the class-vs-instance dichotomy still exists at a conceptual level, even if the language/runtime doesn't make it explicit. My "window" prototype might be the same sort of thing as an individual window, but it would be a big mistake to try to actually display the prototype on the screen. Being explicit about the class-vs-instance dichotomy is a kind of strong typing – the language and runtime is aware of the conceptual distinction; whereas prototype-based languages are comparatively weak typing – the conceptual distinction still exists in the mind of the developer, but the language/runtime doesn't know about it. Class-based languages catch the "trying to use the prototype as if it was an actual instance" bug at compile-time, prototype-based languages leave it to blow up in bizarre ways at runtime (accidentally treating the prototype as an instance can lead to modifying the prototype, which can then cause unpredictable consequences for all the other objects having that prototype.)
One big problem with Java, is the lack of meta-classes. Why can't I subclass Class? There are cases where doing so might be a sensible solution, but Java won't let you. In Java, for my instance methods, I can inherit them from a superclass, or have them implement an interface; but, static class methods cannot be inherited, nor can the static methods of a class implement an interface. This forces people into workarounds such as the factory pattern (which many Java applications/libraries massively overuse), when having a static method implement an interface might be a simpler solution – but that would require us to be able to (effectively) subclass Class. It also produces weird things like 'Class<T>' to mean a class extending class (or implementing interface) T, where meta-classes would offer a much more elegant solution. I don't think the real problem with Java is that it is class-based rather than prototype-based, I think the problem is that its class system is half-baked (no metaclasses, no reified generics, etc)
A point this article raises, which is very true – in Smalltalk, reflection is read-write – modifying classes at runtime is as easy as querying them. Java reflection is essentially read-only. It is possible to do read-write reflection in Java, but it involves immense complexity with source code or byte code generation libraries, custom class loaders, etc – to do something which Smalltalk supports trivially. The argument on the Java side, is that read-write makes optimisations (JIT/etc) a lot harder. No doubt true, but there are alternatives which Java has not pursued – for example, support multiple versions of a class, code is JITted to work with the latest version at JIT time, but other versions are detected and cause a fallback to interpreter mode (possibly followed by re-JITting to add support for that other version.)
- bitwize 4y ago> My "window" prototype might be the same sort of thing as an individual window, but it would be a big mistake to try to actually display the prototype on the screen. This is not how Self works. In Self, it is legitimate to take an existing window, clone it, and change properties and methods to taste. The first window serves as prototype for the second, despite also being a regular window. It's also possible to use what I call the Master Mold pattern (after the Sentinel from Marvel Comics whose only purpose is to make more Sentinels). That is, to construct an object that isn't actually used except as a prototype for other objects. In that case, the prototype will indeed function like a class. But this isn't necessary or even encouraged in Self.
- skissane 4y ago> In Self, it is legitimate to take an existing window, clone it, and change properties and methods to taste. The first window serves as prototype for the second, despite also being a regular window. But doesn't this have the risk, that I change some property on the first window, without realising the second window is using the first as its prototype – and while that property change is beneficial for the first window, it is detrimental to the second? I suppose the same thing can happen in class-based OO – I might make some change to a superclass which is beneficial for one subclass but breaks another. However, I think the class-vs-instance dichotomy helps here – if I'm trying to evaluate whether a change to the superclass might break a subclass, I can focus on the subclasses, and often I can get away with not studying every piece of code which instantiates or uses the class and its subclasses. I can't use that heuristic in prototype-based languages, since the language syntax and semantics don't distinguish unextended uses from extensions. > It's also possible to use what I call the Master Mold pattern (after the Sentinel from Marvel Comics whose only purpose is to make more Sentinels). That is, to construct an object that isn't actually used except as a prototype for other objects. Oh sure, but here we are back to "weak-typing" vs "strong-typing". A class-based language enforces the "Master Mold" pattern, and if you try to violate it, your code won't even compile. A prototype-based language doesn't enforce the pattern, it is just in the programmer's head. Being able to break it in an exceptional case might be helpful; on the other hand, there is the risk the programmer might break it by accident rather than intention, producing bugs which would never happen in a more strongly-typed language.