I think Tassilo's ideas about extenders deserve more discussion. But let me continue the discussion with Armando for now.
Thank you for offering the Eclipse API example, which is very helpful. I agree that Java's OO paradigm creates the need for abstract classes. However, I would like to look further at what is going on in the Eclipse example and consider if protocols in Clojure are the same or different. To clarify the example: Eclipse allows the user to add the user's own methods for certain kinds of event-handling. Because the language is Java, these methods must be attached to a class. In order for Eclipse to specify which methods it expects, Eclipse provides an interface that the user's class must implement. For convenience, Eclipse also provides default implementations of the required methods; these are provided in an abstract class. When the user extends this abstract class, the user can elect to override none, some, or all of the default methods. I have a few observations. 1) In the example, it is certainly true that the user may implement none or just some of the methods of the interface. However, when the user finally passes an object instance to Eclipse, *all* of the methods of the interface are in fact implemented by that instance. This is because the default implementations in the abstract class will fill any holes the user leaves. Therefore, I respectfully disagree that this is case of "partial implementation." It may appear so to the user, but not to the language. 2) It is true that a Java abstract class can partially implement an interface. However, an abstract class cannot be instantiated. It must be extended first. When extending an abstract class, the user must implement any methods that the abstract class has not implemented. No concrete class "partially implements" an interface. To me, the analogy to Java therefore supports the idea that the extends? relationship ought to require a datatype/record to implement all of its protocols' methods. A datatype/record is a piece of data, not a collection of default methods; it is therefore like an object instance, not an abstract class. Object instances must implement all of their interfaces' methods. As I said in my last post, it would seem strange for a language to provide protocols (rather than just fast, un-grouped multimethods) if a piece of data that "satisfies" a protocol does not necessarily implement even one of the protocol's methods. 3) Armando's worry is valid that requiring implementation of protocol methods would result in "proliferation of interfaces and protocols that would prevent reuse; something like ListWithoutModificationOrBulkOperations." However, to me the beauty of protocols is that they can be highly granular. They are not tied to a rigid inheritance paradigm. They support direct implementation composition. If, per Armando's example, a protocol designer felt the need to have a BasicList protocol, a ModifiableList protocol, and a BulkOperations protocol, to be combined as necessary, that would be just fine. 4) In /The Joy of Clojure/, a few of the examples involve extending a type to implement just one of a protocol's multiple methods (pp. 193-195). The authors consider this a valid use case. To leave the /JoC/ examples roughly intact while giving protocols a bit more bite, let me throw out two ideas for discussion. Idea 1 ====== Types may be extended to a strict subset of a protocol's methods, but satisfies? and extends? will return FALSE, not true, in this case. That is: => (defprotocol Fixo (fixo-push [fixo value]) (fixo-pop [fixo])) Fixo => (extend-type clojure.lang.IPersistentVector Fixo (fixo-push [vector value] (conj vector value))) [succeeds] => (extends? Fixo clojure.lang.IPersistentVector) FALSE, not true Idea 2 ====== Types may be extended to new protocols only if *all* of the protocol's methods are implemented. However, types may be always extended tonew, ungrouped *methods*. That is: => (defprotocol Fixo (fixo-push [fixo value]) (fixo-pop [fixo])) Fixo => (extend-type clojure.lang.IPersistentVector Fixo (fixo-push [vector value] (conj vector value))) [should FAIL - protocol name not allowed here unless all methods are implemented] => (extend-type clojure.lang.IPersistentVector nil (fixo-push [vector value] (conj vector value))) [should SUCCEED - fixo-push is like a fast multimethod, without a named protocol] => (extends? Fixo clojure.lang.IPersistentVector) FALSE, not true I invite discussion and criticism. All the best, Garth On Thursday, March 8, 2012 11:24:42 AM UTC-5, Armando Blancas wrote: > > (Don't know why I can only respond to the first message.) > > I come across partial implementation all the time, and with proxy, too. In > Eclipse this is so common that this is typical: > > "This adapter class provides default implementations for the methods > described by the SelectionListener interface. > Classes that wish to deal with SelectionEvents can extend this class and > override only the methods which they are interested in." > > You may have seen the idea of optional operation in java.util.List and how > it's handled in places like AbstractList with default implementations. > Clojure itself has partial implementations of List in PersistentList and > Collection in PersistentQueue. I suppose the alternative would be a > proliferation of interfaces and protocols that would prevent reuse; > something like ListWithoutModificationOrBulkOperations could have a full > implementation but little use. > > I can see the value of your view of protocol as spec, but maybe for > domain-realted concepts and less for plumbing. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en