4 ms·
> This seems completely circular to me. Your definition of OOP is really "whatever all OOP languages do"? It seems logical to me that for two things to be grou
by didibus 8y ago
> This seems completely circular to me. Your definition of OOP is really "whatever all OOP languages do"?
It seems logical to me that for two things to be grouped together in the same category, some commonality between the things are required. So there has to be something in common with all OOP languages which makes them OOP. Otherwise, it is not possible for them all to be OOP languages.
You're saying a language supports OOP if it has a protocol like construct. Sure, you can decide to define OOP as such. But then languages commonly refered to as supporting OOP, like Java, get excluded. So it seems to me it's a bad way to define OOP if it excludes Java, which is probably the most cited language when talking about OOP.
> What is OO to you?
It's a paradigm. Where your program models the problem using a concept known as an object. Characterised by the grouping of data fields and procedures responsible for accessing and modifying said fields. Objects know about both their fields and their methods. Where other code can only access the object's fields and the object's methods through a reference to the object itself. A pure OO language would have everything modeled this way exclusively. Thus nothing would exist outside an object, and you'd only have objects interacting with other objects. An impure OO language, thus a multi-paradigm language with support for OO, would allow part of your code and data to be modeled this way through objects, and other parts using other paradigms.
Without resorting to interop, Clojure/Script, to my knowledge, does not have an object construct which fits this definition.
> from a perspective of message passing. A message is passed to the record and based on its pedigree it decides how to respond
The call isn't made to the record, but to the protocol function. A function is called, and this function decides what to do based on the type of the first argument.
> Plenty of OO languages don't have private data and allow direct access to object fields. Arguably, by your definition, it isn't OO at all to even provide for public fields
Those are impure OO languages, they are multi-paradigm. If you were to make all your data inside objects public, and never put any methods on the objects. And you would put all your methods outside of objects, or inside objects with no data and have them take objects with data as their first argument, then your program is no longer using the OO paradigm, it is no longer object oriented.
> As long as the methods follow the data around or vice versa, in my view, encapsulation has been achieved
Clojure/Script records don't have their methods following them around. You can define the functions for the protocol in another namespace, and then you have to require them separately. They don't tag along with the record.
> A record implementing a protocol can receive method calls. All of its method implementations happen with the records own fields in scope (assuming you define them in the defrecord)
Again, the call is made to the protocol functions. Simply importing the record does not make these functions exist within your namespace, you need to require them. And these functions are not scoped within the record. They don't see the fields, they need to access them through the record, like any other function would. Nothing is special about these functions. They're just functions that know how to do something to a concrete datatype.
> As I understand it, the state of the art in Class-Oriented Programming has been to define interfaces for everything, and then define classes that implement those interfaces, and then use the interface to type functions and methods. A Trait in Rust or a Protocol in Clojure just knocks out the middle man, the unnecessary and unwanted class. What's left is still OO, you can get rid of the class and all of it's baggage and still have plenty of OO left
No, what you're seeing is the evolution of languages away from OO. It is a direct result of the criticism applied to the OO paradigm, and traditionally OO languages recognising its shortcomings, thus evolving other paradigms or practices which bypass the OO model. All these languages are multi-paradigm in essence now. A trait effectively is an alternate concept from an object. It defines functions interface and implementation separately from the datatype. You can have a language with traits yet without objects, such as Clojure/Script. Such a language is not OOP. You can't take away the concept of objects and still be object oriented. This is just support for type polymorphism, and it's ortgogonal to objects.
I think you might be the one with a circular definition of OO. Because you see things in languages that have support for OOP, does not mean all their features are part of the OOP paradigm.
Take Java, remove its support for interfaces, now ask yourself if it still supports object oriented programming? Yes? Why?
At least, this is my taxonomy. But I feel it is also the most common taxonomy in use. And I also feel it is a more useful taxonomy. Things are more granular, more specific. And within this taxonomy, the criticism against OOP is well founded, and more convincing. And it makes sense then that Rust and Haskell and Clojure/Script do not advertise support for OOP. It makes sense why Java, which lacks Traits, is still OOP, etc.
Just my 2 cents.
- likeclockwork 8y agoI am not saying that traits/protocols/interfaces/typeclasses are necessary for OO, I am saying that they are sufficient. The object-like construct in Clojure is an instance of a record or a type that implements a protocol. (A deftyped type can even contain mutable fields!) It doesn't matter where the methods live, they apply only to objects that implement the protocol. That's behavior and data bundled together. As far as I am concerned that is an object. A trait is just a different expression of an object from an instance of a class. The interface being defined separately changes nothing; interfaces/traits/typeclasses offer different advantages than subtype polymorphism and inheritance hierarchies. Would you say CLOS isn't OO? A trait isn't an object and neither is a class. An instance of a data type that implements a trait is as much an object as an instance of a class. Rust, Haskell, and Clojure don't advertise support for OOP because in the common taxonomy an object is defined as "an instance of a class". > Clojure/Script records don't have their methods following them around. You can define the functions for the protocol in another namespace, and then you have to require them separately. They don't tag along with the record. > Again, the call is made to the protocol functions. Simply importing the record does not make these functions exist within your namespace, you need to require them. And these functions are not scoped within the record. They don't see the fields, they need to access them through the record, like any other function would. Nothing is special about these functions. They're just functions that know how to do something to a concrete datatype. 1. Nothing is special about methods in "OOP" languages either, aside from the dot operator and implicit this. There are object systems that have neither. 2. You have made a factual error here. An implemention of a protocol on a record or a deftyped type(is there a proper non-generic name for this?) DOES have implicit access to the fields of the record or type. (defprotocol Container (inventory [this]) (add [this thing])) (defrecord Box [contents] Container (inventory [this] contents) (add [this thing] (update this :contents conj thing))) boot.user=> (-> (->Box [1 2 3]) (add 4) (inventory)) ;; => [1 2 3 4] The protocol method clearly has access to the contents field of the record, scoped by the record definition. The 'this' argument is a convenience to allow reconstituting the immutable object for return. You could just as easily have been forced to call the constructor again in order to reconstitute the object. If the record contained an atom it would happily carry around mutable state and there'd be no need to reconstitute anything. Building on my previous example: (deftype MutableBox [^{:volatile-mutable true} contents] Container (inventory [this] contents) (add [this thing] (set! contents (conj contents thing)))) boot.user=> (let [b (->MutableBox [1 2 3])] (add b 4) (inventory b)) ;; => [1 2 3 4] A type with mutable state, its fields in scope. This isn't even used. You're correct that the call IS made to the protocol method. And yes, not having the protocol in scope prevents from calling the object's methods. One might even say that methods you don't know about and thus can't refer to are encapsulated. It's an extremely flexible object system--one that eschews mutable state and works well with a functional style and prefix syntax--but it's still an object system. Object isn't a keyword in any of the languages we've mentioned. But we know how to find them and what they are. I don't understand why you can't see this one.