Re: Why don't extends? and satisfies? require implementation of all protocol methods?
I just want to point out that I incorrectly stated that Clojure generates abstract stubs for unimplemented protocol methods (or types, for that matter). In fact, Clojure does nothing about them. For classes to fully implement interfaces is enforced by the Java compiler, not the JVM. On Thursday, March 8, 2012 11:40:20 PM UTC-8, Garth Sheldon-Coulson wrote: 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
Re: Why don't extends? and satisfies? require implementation of all protocol methods?
(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
Re: Why don't extends? and satisfies? require implementation of all protocol methods?
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
Re: Why don't extends? and satisfies? require implementation of all protocol methods?
Garth Sheldon-Coulson garth.sheldoncoul...@gmail.com writes: Hi Garth, I'm returning to Clojure in earnest for the first time since 1.1 (and very happy to be back!). Apologies if this question revisits old issues. I've revisited mostly the same 2 days ago: 87399ocg9a@thinkpad.tsdh.de I'm trying to understand why the semantics of protocols are such that the third statement here returns true: user= (defprotocol Bashable (bash [this]) (boom [this])) Bashable user= (defrecord Record [] Bashable (bash [this] bash!)) user.Record user= (and (satisfies? Bashable (Record.)) (extends? Bashable Record)) true This returns true even though boom is not implemented for Record: user= (boom (Record.)) AbstractMethodError user.Record.boom()Ljava/lang/Object; user/eval55 (NO_SOURCE_FILE:3) Apparently, types/records can implement a protocol in name only. What do you mean with in name only? What is behind this choice? Intuitively, I would have conceived of a protocol as a collection of methods all of which must be implemented in order for a type/record to extend the protocol. I think it would be nice if extends?/satisfies? would return the set of implemented methods, say, as keywords. Then you could check if an object implements exactly the methods that your function wants to call. A second question. How does one explicitly extend a protocol per the documentation of the extenders function? Intuitively, I would have thought that the code above explicitly extends Bashable if it extends Bashable at all. Yet: user= (extenders Bashable) nil That was my initial question in my posting two days ago. extenders returns only types that extend a protocol using the `extend' function (or `extend-type', `extend-protocol' macros). So this works: user (defprotocol Bashable (bash [this]) (boom [this])) Bashable user (defrecord Record []) user.Record user (extend-protocol Bashable Record (bash [_] Bash!)) nil user (extenders Bashable) (user.Record) IMO, that behavior is not very obvious. OTOH, Stu said that extenders is specific to the extend form (as suggested by its name), and in fact, neither defrecord's nor deftype's documentation speak of extending a protocol, but their terminology is to supply method implementations for protocol methods. But then, what's the use case of `extenders' anyway? I mean, I want to know what types participate in a protocol. I don't care if they do because they were explicitly extended to the protocol or because the method implementations were provided directly in their definition form. That's an implementation detail I shouldn't have to bother with... Bye, Tassilo -- 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
Re: Why don't extends? and satisfies? require implementation of all protocol methods?
When you create a protocol, as an implementation detail, it also creates a Java interface. When you list protocols in a deftype or defrecord form, the generated class actually implements that Java interface. And protocol calls to that type call through the interface. This gives the best performance. If you add protocols to an existing class, record or type; this dispatch is done dynamically and interfaces aren't involved. -- Dave On Mar 7, 2012 8:09 AM, Tassilo Horn tass...@member.fsf.org wrote: Garth Sheldon-Coulson garth.sheldoncoul...@gmail.com writes: Hi Garth, I'm returning to Clojure in earnest for the first time since 1.1 (and very happy to be back!). Apologies if this question revisits old issues. I've revisited mostly the same 2 days ago: 87399ocg9a@thinkpad.tsdh.de I'm trying to understand why the semantics of protocols are such that the third statement here returns true: user= (defprotocol Bashable (bash [this]) (boom [this])) Bashable user= (defrecord Record [] Bashable (bash [this] bash!)) user.Record user= (and (satisfies? Bashable (Record.)) (extends? Bashable Record)) true This returns true even though boom is not implemented for Record: user= (boom (Record.)) AbstractMethodError user.Record.boom()Ljava/lang/Object; user/eval55 (NO_SOURCE_FILE:3) Apparently, types/records can implement a protocol in name only. What do you mean with in name only? What is behind this choice? Intuitively, I would have conceived of a protocol as a collection of methods all of which must be implemented in order for a type/record to extend the protocol. I think it would be nice if extends?/satisfies? would return the set of implemented methods, say, as keywords. Then you could check if an object implements exactly the methods that your function wants to call. A second question. How does one explicitly extend a protocol per the documentation of the extenders function? Intuitively, I would have thought that the code above explicitly extends Bashable if it extends Bashable at all. Yet: user= (extenders Bashable) nil That was my initial question in my posting two days ago. extenders returns only types that extend a protocol using the `extend' function (or `extend-type', `extend-protocol' macros). So this works: user (defprotocol Bashable (bash [this]) (boom [this])) Bashable user (defrecord Record []) user.Record user (extend-protocol Bashable Record (bash [_] Bash!)) nil user (extenders Bashable) (user.Record) IMO, that behavior is not very obvious. OTOH, Stu said that extenders is specific to the extend form (as suggested by its name), and in fact, neither defrecord's nor deftype's documentation speak of extending a protocol, but their terminology is to supply method implementations for protocol methods. But then, what's the use case of `extenders' anyway? I mean, I want to know what types participate in a protocol. I don't care if they do because they were explicitly extended to the protocol or because the method implementations were provided directly in their definition form. That's an implementation detail I shouldn't have to bother with... Bye, Tassilo -- 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 -- 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
Re: Why don't extends? and satisfies? require implementation of all protocol methods?
David Powell d...@djpowell.net writes: Hi David, When you create a protocol, as an implementation detail, it also creates a Java interface. Is a protocal neccessarily an implementation detail? I mean, it might be, but it can also be a public specification of the requirements types have to satisfy in order to integrate with my lib. When you list protocols in a deftype or defrecord form, the generated class actually implements that Java interface. And protocol calls to that type call through the interface. This gives the best performance. If you add protocols to an existing class, record or type; this dispatch is done dynamically and interfaces aren't involved. That suggests that `extenders' is actually a private function for doing the dynamic dispatch. But it's not used at all in clojure.core. And it's inconsistent with extends?. user (defrecord R2 [] Bashable (boom [this] :boom)) user.R2 user (extenders Bashable);; just returns the dynamic extenders (user.Record) user (extends? Bashable R2) ;; does an implements? check as well true IMHO, `extenders' should return also types implementing the protocol interface directly, so that (extends? P T) = (some #(= % T) (extenders P)) holds. Bye, Tassilo -- 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
Re: Why don't extends? and satisfies? require implementation of all protocol methods?
On Mar 7, 1:09 am, Tassilo Horn tass...@member.fsf.org wrote: David Powell d...@djpowell.net writes: Hi David, When you create a protocol, as an implementation detail, it also creates a Java interface. Is a protocal neccessarily an implementation detail? I mean, it might be, but it can also be a public specification of the requirements types have to satisfy in order to integrate with my lib. You misread his assertion. The implementation detail is that an interface is defined along with each protocol. When you list protocols in a deftype or defrecord form, the generated class actually implements that Java interface. And protocol calls to that type call through the interface. This gives the best performance. If you add protocols to an existing class, record or type; this dispatch is done dynamically and interfaces aren't involved. That suggests that `extenders' is actually a private function for doing the dynamic dispatch. But it's not used at all in clojure.core. And it's inconsistent with extends?. I don't really see how it suggests that, but okay. It does appear that you're right that extenders and extends? are not consistent between each other. user (defrecord R2 [] Bashable (boom [this] :boom)) user.R2 user (extenders Bashable) ;; just returns the dynamic extenders (user.Record) user (extends? Bashable R2) ;; does an implements? check as well true IMHO, `extenders' should return also types implementing the protocol interface directly, so that (extends? P T) = (some #(= % T) (extenders P)) holds. You're entitled to that humble opinion, but it's not really possible with any kind of reasonable performance. When you implement the protocol interface directly, no machinery in clojure.core gets involved at all - you just define a class that implements an interface. For `extenders` to be able to list Java classes that implement the interface directly, it would have to walk through every class loaded in the JVM and check its implemented-interfaces. Of course, you could make it almost-work, by having deftype/defrecord emit some special code for hooking into extenders whenever you define a class in Clojure, but then it would only work for classes defined within Clojure, and not for Java classes that implement Clojure's interfaces/protocols. Frankly I think that's worse than just limiting it to dynamic extenders. -- 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
Re: Why don't extends? and satisfies? require implementation of all protocol methods?
Alan Malloy a...@malloys.org writes: Hi Alan, IMHO, `extenders' should return also types implementing the protocol interface directly [i.e., deftypes and defrecords], so that (extends? P T) = (some #(= % T) (extenders P)) holds. You're entitled to that humble opinion, but it's not really possible with any kind of reasonable performance. When you implement the protocol interface directly, no machinery in clojure.core gets involved at all - you just define a class that implements an interface. For `extenders` to be able to list Java classes that implement the interface directly, it would have to walk through every class loaded in the JVM and check its implemented-interfaces. Of course, you could make it almost-work, by having deftype/defrecord emit some special code for hooking into extenders whenever you define a class in Clojure, That's what I had in mind. deftype/defprotocol could just alter the :impls of the protocol, just like `extend' does. but then it would only work for classes defined within Clojure, and not for Java classes that implement Clojure's interfaces/protocols. Well, yes. Frankly I think that's worse than just limiting it to dynamic extenders. I'd prefer if it was limited to extenders that extend within clojure, because that seems to be the more common case. My use case for `extenders' was my API docs generator, where I'd like to add to every protocol description the types that extend that protocol. So basically, I see 3 options: 1) rename `extenders' to `dynamic-extenders' (or something like that) to get rid of the extends?/extenders inconsistency 2) remove `extenders' completely (can anyone think of any use case for it in its current incarnation that is important enough that it justifies a core function instead of using (keys (:impls protocol)) directly?) 3) make deftype/defrecord alter the :impls just like `extend' does and document the limitation concerning implementing protocol interfaces in java explicitly in the `extenders' docstring I'm happy to provide a patch for either option. Bye, Tassilo -- 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
Re: Why don't extends? and satisfies? require implementation of all protocol methods?
user= (boom (Record.)) AbstractMethodError user.Record.boom()Ljava/lang/Object; user/eval55 (NO_SOURCE_FILE:3) Apparently, types/records can implement a protocol in name only. That can't in name only since you obviously got an implementation, though abstract. What is behind this choice? That's convenient when an implementation only cares about a few methods in a big protocol. -- 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
Why don't extends? and satisfies? require implementation of all protocol methods?
I'm returning to Clojure in earnest for the first time since 1.1 (and very happy to be back!). Apologies if this question revisits old issues. I'm trying to understand why the semantics of protocols are such that the third statement here returns true: user= (defprotocol Bashable (bash [this]) (boom [this])) Bashable user= (defrecord Record [] Bashable (bash [this] bash!)) user.Record user= (and (satisfies? Bashable (Record.)) (extends? Bashable Record)) true This returns true even though boom is not implemented for Record: user= (boom (Record.)) AbstractMethodError user.Record.boom()Ljava/lang/Object; user/eval55 (NO_SOURCE_FILE:3) Apparently, types/records can implement a protocol in name only. What is behind this choice? Intuitively, I would have conceived of a protocol as a collection of methods all of which must be implemented in order for a type/record to extend the protocol. A second question. How does one explicitly extend a protocol per the documentation of the extenders function? Intuitively, I would have thought that the code above explicitly extends Bashable if it extends Bashable at all. Yet: user= (extenders Bashable) nil Thank you. All the best, Garth Sheldon-Coulson -- 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