3 ms·
I 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 i
by likeclockwork 8y ago
I 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.
- didibus 8y ago> The protocol method clearly has access to the contents field of the record, scoped by the record definition. Thank you for pointing this out. I have learned something today. I didn't know that protocols implemented inline within the deftype and defrecord macros were actually given direct access to the fields. And even more surprising to me, that mutable fields on deftype are actually made package restricted. Well then, I have to concede it to you, even within my definition of OOP, deftype is now an equivalent construct to my concept of objects. Those methods are no longer trait like, they're full blown object methods, with special privileges, and tight coupling to the mutable data fields. They can not be defined separately to the type, and a type can not be extended outside its definition to support such methods. > I am not saying that traits/protocols/interfaces/typeclasses are necessary for OO, I am saying that they are sufficient. > 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. So how would you define OO? I get it your are in the camp that equal polymorphism to OO? And by the way, I do see it. Clojure/Script has object-based constructs. If you think of the more general sense for object, which is identity + attributes, records fit the bill. This is defined here https://en.wikipedia.org/wiki/Object_(computer_science)#Object-based_languages https://en.wikipedia.org/wiki/Object_(computer_science)#Obje.... Now, I also agree with you, it even has a construct that groups data and behavior together in the form of deftype with mutable fields. So at this point, it has support for OOP. What I'm really arguing is just a taxonomy. I see three distinct characteristics. The grouping of data and behavior together behind an object (OO). The ability to dispatch to different implementations of a procedure based on the datatype (type polymorphism). And the ability to inherit behavior from the behavior of an associated parent (inheritance). And this is my preferred taxonomy. In that sense, prior to me learning about deftype with mutable fields, I saw Clojure/Script as supporting type polymorphism using either protocols or multi-methods. And inheritance through multi-methods. But I did not see anything to group data and behavior together behind an object. Thus I did not consider it had support for OO. I know some people say polymorphism alone is the essence of OO. Thus Haskell, Rust, Clojure/Scipt, Go, PureScript, and others are all OO languages. I don't know though, I feel the average dev doesn't think of it that way. Other people think of OO as the bundling of data and behavior, and that seemed much more reasonable to me. This is how the Gang of Four book defines OO for example. And this is how I like to think of it also. But, after this conversation, I wonder if I don't prefer the definition from the wiki link I shared. Where OOP is the combination of all three. A single construct which groups data and behavior, and allows polymorphism as well as inheritance. And this is what is known as an object, and all three must be present to be considered OOP. With this definition, I think Clojure/Script wouldn't fit as OOP. Since, and tell me otherwise, deftype doesn't support inheritance. And multi-methods don't group data and behavior.