Re: Datatypes and Protocols update
On May 22, 9:51 pm, Pedro Teixeira pedr...@gmail.com wrote: On Apr 27, 9:45 am, Rich Hickey richhic...@gmail.com wrote: On Apr 27, 2010, at 3:20 AM, Mark Engelberg wrote: Watching Stuart's tutorial, it looks like the automaticfactory functions for deftypes have gone away (I'm still working with Clojure 1.1, so haven't had a chance to try the latest changes for myself). I'm going to miss that feature, especially fordefrecord, which is now the common case construct. I understand that you can always do Foo. to construct a Foo record, but these constructors don't act as full-fledged functions, right? Honestly, for me the main issue is just that subjectively, it is less satisfying to create a Clojure data structure and end up with something that you construct with Java interop syntax. I'd like Clojure data structures to look and feel Clojurish not Javaish (yes, I know that Clojure is built with interop in mind, so technically, anything Javaish is also Clojurish, but I still feel a difference). I agree. I asked for suggestions for thefactoryfn in #clojure, but got some pushback against introducing things in the namespace. I'm still open to suggestions, here are the issues: The generated type name is now imported, so at the very least the factoryfn can't have the same name as the class. Alternatives are create-Foo etc. Some have asked for parameterized factories: (record Foo ...) or (record ::Foo ...) These cannot be made as fast as direct factories. Also, they may be used for key/value initialization: (record ::Foo :field1 v1 :field2 v2 ...) As soon as people want bodies for the factories, in order to do argument transformation/validation/defaulting, a generatedfactoryis in the way. We left it at: If you really want afactoryyou can always write one, let's see if people do. Thought a dynamic factory is usefull. Here it goes my first macro, so please let me know if I'm silly here. (defmacro record Dynamic factory for defrecords. ([name] `(record ~name {}) ) ([name vals-map] (let [num-fields (alength (.getParameterTypes (first (.getDeclaredConstructors (class name) args (repeat num-fields nil)] `(merge (new ~name ~...@args) ~vals-map Usage: (defrecord Bar [x y]) (record Bar) That previous attempt was not good. For any one interested, the following macro might do the job: (defmacro record Dynamic factory for defrecords. ([name] `(record ~name {}) ) ([name vals-map] `(let [con# (first (.getDeclaredConstructors ~name)) num# (alength (.getParameterTypes con#))] (merge (.newInstance con# (make-array Object num#)) ~vals-map So we can maintain a dynamic relation with defrecord: (defrecord Bar [x y]) (record Bar) (record Bar {:x 1)) cheers, Pedro -- 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: Datatypes and Protocols update
On Apr 27, 9:45 am, Rich Hickey richhic...@gmail.com wrote: On Apr 27, 2010, at 3:20 AM, Mark Engelberg wrote: Watching Stuart's tutorial, it looks like the automaticfactory functions for deftypes have gone away (I'm still working with Clojure 1.1, so haven't had a chance to try the latest changes for myself). I'm going to miss that feature, especially fordefrecord, which is now the common case construct. I understand that you can always do Foo. to construct a Foo record, but these constructors don't act as full-fledged functions, right? Honestly, for me the main issue is just that subjectively, it is less satisfying to create a Clojure data structure and end up with something that you construct with Java interop syntax. I'd like Clojure data structures to look and feel Clojurish not Javaish (yes, I know that Clojure is built with interop in mind, so technically, anything Javaish is also Clojurish, but I still feel a difference). I agree. I asked for suggestions for thefactoryfn in #clojure, but got some pushback against introducing things in the namespace. I'm still open to suggestions, here are the issues: The generated type name is now imported, so at the very least the factoryfn can't have the same name as the class. Alternatives are create-Foo etc. Some have asked for parameterized factories: (record Foo ...) or (record ::Foo ...) These cannot be made as fast as direct factories. Also, they may be used for key/value initialization: (record ::Foo :field1 v1 :field2 v2 ...) As soon as people want bodies for the factories, in order to do argument transformation/validation/defaulting, a generatedfactoryis in the way. We left it at: If you really want afactoryyou can always write one, let's see if people do. Thought a dynamic factory is usefull. Here it goes my first macro, so please let me know if I'm silly here. (defmacro record Dynamic factory for defrecords. ([name] `(record ~name {}) ) ([name vals-map] (let [num-fields (alength (.getParameterTypes (first (.getDeclaredConstructors (class name) args (repeat num-fields nil)] `(merge (new ~name ~...@args) ~vals-map Usage: (defrecord Bar [x y]) (record Bar) cheers, Pedro -- 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: Datatypes and Protocols update
Watching Stuart's tutorial, it looks like the automatic factory functions for deftypes have gone away (I'm still working with Clojure 1.1, so haven't had a chance to try the latest changes for myself). I'm going to miss that feature, especially for defrecord, which is now the common case construct. I understand that you can always do Foo. to construct a Foo record, but these constructors don't act as full-fledged functions, right? Honestly, for me the main issue is just that subjectively, it is less satisfying to create a Clojure data structure and end up with something that you construct with Java interop syntax. I'd like Clojure data structures to look and feel Clojurish not Javaish (yes, I know that Clojure is built with interop in mind, so technically, anything Javaish is also Clojurish, but I still feel a difference). -- 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: Datatypes and Protocols update
On 27 Apr 2010, at 09:20, Mark Engelberg wrote: I understand that you can always do Foo. to construct a Foo record, but these constructors don't act as full-fledged functions, right? No. They are not first-class objects (in fact, not objects at all in the JVM sense), so you can't pass them around. Honestly, for me the main issue is just that subjectively, it is less satisfying to create a Clojure data structure and end up with something that you construct with Java interop syntax. I'd like Clojure data structures to look and feel Clojurish not Javaish That was my initial reaction as well. However, I just write my own factory functions now, and this gives me the opportunity to add argument validation in any way I like. BTW, another change is that the defined type is a Java class, whereas before it was a var pointing to the factory function. Not being a var means that the type doesn't really reside in the namespace. In particular, a :use of the namespace doesn't get you the type, you have to :import it. Taken together, these changes make deftype and defrecord low-level features for defining data types that are best exposed to the outside world via a functional API. Your clients don't need to know that there's a Java type with a constructor hidden somewhere. Konrad. -- 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: Datatypes and Protocols update
On Apr 27, 2010, at 3:20 AM, Mark Engelberg wrote: Watching Stuart's tutorial, it looks like the automatic factory functions for deftypes have gone away (I'm still working with Clojure 1.1, so haven't had a chance to try the latest changes for myself). I'm going to miss that feature, especially for defrecord, which is now the common case construct. I understand that you can always do Foo. to construct a Foo record, but these constructors don't act as full-fledged functions, right? Honestly, for me the main issue is just that subjectively, it is less satisfying to create a Clojure data structure and end up with something that you construct with Java interop syntax. I'd like Clojure data structures to look and feel Clojurish not Javaish (yes, I know that Clojure is built with interop in mind, so technically, anything Javaish is also Clojurish, but I still feel a difference). I agree. I asked for suggestions for the factory fn in #clojure, but got some pushback against introducing things in the namespace. I'm still open to suggestions, here are the issues: The generated type name is now imported, so at the very least the factory fn can't have the same name as the class. Alternatives are create-Foo etc. Some have asked for parameterized factories: (record Foo ...) or (record ::Foo ...) These cannot be made as fast as direct factories. Also, they may be used for key/value initialization: (record ::Foo :field1 v1 :field2 v2 ...) As soon as people want bodies for the factories, in order to do argument transformation/validation/defaulting, a generated factory is in the way. We left it at: If you really want a factory you can always write one, let's see if people do. Rich -- 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: Datatypes and Protocols update
On 27.04.2010, at 14:45, Rich Hickey wrote: I agree. I asked for suggestions for the factory fn in #clojure, but got some pushback against introducing things in the namespace. I'm still open to suggestions, here are the issues: The generated type name is now imported, so at the very least the factory fn can't have the same name as the class. Alternatives are create-Foo etc. How about providing the name of a factory function via an option, with no factory as the default? Something like (defrecord Foo [bar baz] :factory create-Foo) This would avoid the creation of vars that are not explicitly named in the code, and thus bad surprises. Konrad. -- 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: Datatypes and Protocols update
On Apr 23, 2010, at 11:48 PM, Mark Engelberg wrote: A few meandering observations: I like the latest change to include a this argument. It makes the number of arguments line up which is a good thing. I like the idea of defrecord for the common case, rather than having to request default implementations of various interfaces within deftype. Still, I think the splitting of deftype and defrecord has merely delayed, not eliminated, the need to eventually find a convenient, general scheme for layering in default implementations of interfaces. I look forward to seeing how this evolves. I like Rich's description of Clojure's datatypes/protocols as opinionated, and mostly agree with his manifesto. The one opinion I have the most difficulty with is: It has always been an unfortunate characteristic of using classes for application domain information that it resulted in information being hidden behind class-specific micro-languages, e.g. even the seemingly harmless employee.getName() is a custom interface to data. Putting information in such classes is a problem, much like having every book being written in a different language would be a problem. You can no longer take a generic approach to information processing. This results in an explosion of needless specificity, and a dearth of reuse. OO programmers write employee.getName() methods to preserve the option of doing something more sophisticated later, which isn't readily possible once all your clients start using employee.name everywhere. So if you really want to create a generic approach to information processing, it seems like the best approach is to address the underlying reason that these custom interfaces are needed. For this reason, I've always found appealing languages which let you optionally write getter/setter methods that hook into the standard field access syntax. This lets you start out with your fields public, and let your clients use the standard field access interface. Later, if you realize you need to do something special, you can easily add a custom getter without breaking your clients. As far as I know, Clojure doesn't currently make any attempt to address this problem of allowing a standard way to access public data from an object, while preserving the option of doing something more sophisticated later. So a programmer is still forced to choose between the convenience of keyword lookup of data, versus a protocol filled with get-name functions to preserve future flexibility. Ideally, I'd like to see a way to allow me to write a program using (:name employee), and later, if I need to, customize the employee datatype so that (:name employee) actually dispatches to some other function. Ditto with (assoc employee :name Mark) (for example, to validate the data in some way?). If I'm off-base, and these issues are easy to currently workaround in Clojure, I'd love to hear more about how others are handling this in their own programs. You can use functions if you don't like unencapsulated data. Weren't you also advocating for serialization? Such things become much simpler when one knows they are dealing with just data, and not the results of some (possibly irreversible) function, of some other possibly uninitialized dependents etc. You need a level that is the data (even under some 'property' system). The keyword-accessible, constructor-mapped fields are that layer. You can always build functions on top of that. Calculated getters/setters are one of those rich sources of incidental complexity, with little real-world benefit IMO. That said, defrecord is simply implemented in terms of deftype. People can easily prototype alternatives for consideration. Rich -- 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: Datatypes and Protocols update
On Apr 24, 2010, at 1:11 PM, Richard Newman wrote: Neither of those attributes reveal information about the implementation of the objects in question. They both reveal information about the state that some client could find useful. They are both values that, if not directly available from the object should be calculated by the object, as calculating the value requires knowledge about the implementation. This approach still isn't 'good' OO -- it might not leak implementation details, but it supports asking, not telling. http://pragprog.com/articles/tell-dont-ask That is, you should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do. To the extent OO tries to make information into objects, it isn't 'good', IMO. That article is a good example of how goofy things get when you try to turn information into objects. Information is data. Records are for information. Why ever would I want to try to 'tell' a piece of information anything? The datatype and protocol system is there to support the polymorphic manipulation of data by functions, not for recreating OO rabbit holes. Rich -- 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: Datatypes and Protocols update
On 24.04.2010, at 05:48, Mark Engelberg wrote: As far as I know, Clojure doesn't currently make any attempt to address this problem of allowing a standard way to access public data from an object, while preserving the option of doing something more sophisticated later. So a programmer is still forced to choose between the convenience of keyword lookup of data, versus a protocol filled with get-name functions to preserve future flexibility. This is basically a question of how you choose your abstractions (the documented interfaces) and your implementations (the data structures), and in particular on where you draw the boundaries. That is a design issue, so I don't expect any language feature to remove the necessity to think about this carefully. For this reason, I've always found appealing languages which let you optionally write getter/setter methods that hook into the standard field access syntax. That's little more than syntactic sugar, considering that field access is equivalent to calling a method with no arguments. The closest equivalent in Clojure would be to define a functional accessor API for all access to your data. A first implementation would simply set the accessor to be a field-name keyword: (defrecord foo [bar baz]) (def get-bar :bar) (def get-baz :baz) If necessary you can then replace the accessor functions by something more complicated, or turn them into prototype functions to allow multiple implementations. Client code won't see the difference. Konrad. -- 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: Datatypes and Protocols update
You can no longer take a generic approach to information processing. This results in an explosion of needless specificity, and a dearth of reuse. For this reason, I've always found appealing languages which let you optionally write getter/setter methods that hook into the standard field access syntax. This lets you start out with your fields public, and let your clients use the standard field access interface. Later, if you realize you need to do something special, you can easily add a custom getter without breaking your clients. I'd argue that leaky abstractions like getter/setter methods are evil, and a good article (from a Java/imperative perspective) describing why can be found here: http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html?page=1 I think that the quote above from Rich is another good description of why getter/setter methods are bad from a functional perspective. -- 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: Datatypes and Protocols update
On Sat, 24 Apr 2010 06:51:18 -0700 (PDT) MarkSwanson mark.swanson...@gmail.com wrote: You can no longer take a generic approach to information processing. This results in an explosion of needless specificity, and a dearth of reuse. For this reason, I've always found appealing languages which let you optionally write getter/setter methods that hook into the standard field access syntax. This lets you start out with your fields public, and let your clients use the standard field access interface. Later, if you realize you need to do something special, you can easily add a custom getter without breaking your clients. I'd argue that leaky abstractions like getter/setter methods are evil, and a good article (from a Java/imperative perspective) describing why can be found here: http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html?page=1 I think his fundamental assumption - that getter/setters reveal details of the implementation - is wrong. They *can* do that, and possibly in the Java world they normally do do that. But properly used, attributes reveal information about the *state* of the object, not about the implementation. Classic examples would be a queue object with a length attribute, or a financial account object with a balance attribute. Neither of those attributes reveal information about the implementation of the objects in question. They both reveal information about the state that some client could find useful. They are both values that, if not directly available from the object should be calculated by the object, as calculating the value requires knowledge about the implementation. I think that the quote above from Rich is another good description of why getter/setter methods are bad from a functional perspective. Given that your attributes that aren't just random variables from the implementation, but are reasonable things for a client to want to know, then the unattributed inner quote nailed it: languages that don't distinguish between reading the value of an attribute and invoking a zero-argument method - or at least allow you to make the latter look like the former - hide that specificity and encourage reuse. mike -- Mike Meyer m...@mired.org http://www.mired.org/consulting.html Independent Network/Unix/Perforce consultant, email for more information. O ascii ribbon campaign - stop html mail - www.asciiribbon.org -- 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: Datatypes and Protocols update
A lot of these arguments go away with functional programming. With a functional queue, you might as well store the length as a value attribute because it won't ever change. In some cases I can see the argument for on-demand computation of fields with referentially transparent caching. That's where the delay form comes in. If you're using the delay form to hide a very expensive computation, it's probably good to express that in the interface by requiring explicit forcing, rather than having an innocent-looking map lookup take arbitrary time to execute because it forces the evaluation of a delayed expression. -Per On Sat, Apr 24, 2010 at 9:39 PM, Mike Meyer mwm-keyword-googlegroups.620...@mired.org wrote: On Sat, 24 Apr 2010 06:51:18 -0700 (PDT) MarkSwanson mark.swanson...@gmail.com wrote: You can no longer take a generic approach to information processing. This results in an explosion of needless specificity, and a dearth of reuse. For this reason, I've always found appealing languages which let you optionally write getter/setter methods that hook into the standard field access syntax. This lets you start out with your fields public, and let your clients use the standard field access interface. Later, if you realize you need to do something special, you can easily add a custom getter without breaking your clients. I'd argue that leaky abstractions like getter/setter methods are evil, and a good article (from a Java/imperative perspective) describing why can be found here: http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html?page=1 I think his fundamental assumption - that getter/setters reveal details of the implementation - is wrong. They *can* do that, and possibly in the Java world they normally do do that. But properly used, attributes reveal information about the *state* of the object, not about the implementation. Classic examples would be a queue object with a length attribute, or a financial account object with a balance attribute. Neither of those attributes reveal information about the implementation of the objects in question. They both reveal information about the state that some client could find useful. They are both values that, if not directly available from the object should be calculated by the object, as calculating the value requires knowledge about the implementation. I think that the quote above from Rich is another good description of why getter/setter methods are bad from a functional perspective. Given that your attributes that aren't just random variables from the implementation, but are reasonable things for a client to want to know, then the unattributed inner quote nailed it: languages that don't distinguish between reading the value of an attribute and invoking a zero-argument method - or at least allow you to make the latter look like the former - hide that specificity and encourage reuse. mike -- Mike Meyer m...@mired.org http://www.mired.org/consulting.html Independent Network/Unix/Perforce consultant, email for more information. O ascii ribbon campaign - stop html mail - www.asciiribbon.org -- 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: Datatypes and Protocols update
Neither of those attributes reveal information about the implementation of the objects in question. They both reveal information about the state that some client could find useful. They are both values that, if not directly available from the object should be calculated by the object, as calculating the value requires knowledge about the implementation. This approach still isn't 'good' OO -- it might not leak implementation details, but it supports asking, not telling. http://pragprog.com/articles/tell-dont-ask That is, you should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do. -- 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: Datatypes and Protocols update
On 22 Apr 2010, at 21:15, Konrad Hinsen wrote: I have several former deftypes that are a perfect fit for the new defrecord, except that they need a specific comparison function. This is usually for excluding some fields from equality testing, or for requiring identity rather than equality for some fields. What I'd actually like to have for defrecord is an equality protocol that I can implement myself or use a default implementation. That protocol would have a function equals guaranteed to be called only if the object compared to is of the same type. Object.equals would take care of type testing and then call the protocol function. That would remove the check-type-before-comparing-fields overhead that every equals implementation for deftype and defrecord currently requires. Konrad. -- 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: Datatypes and Protocols update
One problem with requiring the other object to be of the same type is that it would break the current model, e.g.: user= (= '(1 2 3) [1 2 3]) true I'm left to wonder if it the more correct implementation of the desired behavior is not to override the equals, but rather to implement Comparable. On Apr 23, 12:12 am, Konrad Hinsen konrad.hin...@fastmail.net wrote: On 22 Apr 2010, at 21:15, Konrad Hinsen wrote: I have several former deftypes that are a perfect fit for the new defrecord, except that they need a specific comparison function. This is usually for excluding some fields from equality testing, or for requiring identity rather than equality for some fields. What I'd actually like to have for defrecord is an equality protocol that I can implement myself or use a default implementation. That protocol would have a function equals guaranteed to be called only if the object compared to is of the same type. Object.equals would take care of type testing and then call the protocol function. That would remove the check-type-before-comparing-fields overhead that every equals implementation for deftype and defrecord currently requires. Konrad. -- 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 athttp://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: Datatypes and Protocols update
On Apr 22, 2010, at 3:15 PM, Konrad Hinsen wrote: On 22.04.2010, at 18:53, Rich Hickey wrote: Feedback and errata welcome as always, One feature in the deftype/defrecord split that I regret is that defrecord no longer allows the redefinition of equals and hashCode. Any attempt to override those results in an error message about duplicate method definitions. I have several former deftypes that are a perfect fit for the new defrecord, except that they need a specific comparison function. This is usually for excluding some fields from equality testing, or for requiring identity rather than equality for some fields. It is a design goal of defrecord that the types it creates have uniform semantics. As soon as you have user-defined equality you can get broken semantics. As it is, I must convert all these to the new deftype, losing the convient field access through keywords. You can get that back easily by implementing ILookup/IKeywordLookup, as does defrecord. I still have ideas about macro-like mixins for use in deftype, but I concluded the mandatory use of such mixins to create record-like things was too much user effort, and we need more experience with code reuse in deftype to determine need. Rich -- 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: Datatypes and Protocols update
On Apr 23, 2010, at 3:12 AM, Konrad Hinsen wrote: On 22 Apr 2010, at 21:15, Konrad Hinsen wrote: I have several former deftypes that are a perfect fit for the new defrecord, except that they need a specific comparison function. This is usually for excluding some fields from equality testing, or for requiring identity rather than equality for some fields. What I'd actually like to have for defrecord is an equality protocol that I can implement myself or use a default implementation. That protocol would have a function equals guaranteed to be called only if the object compared to is of the same type. Object.equals would take care of type testing and then call the protocol function. That would remove the check-type-before- comparing-fields overhead that every equals implementation for deftype and defrecord currently requires. There's not much 'overhead' to that check. Rich -- 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: Datatypes and Protocols update
On Apr 22, 2010, at 1:30 PM, Mark Engelberg wrote: I tried using deftype relatively recently, but realized it wouldn't work for my needs because serialization via *print-dup* wasn't yet implemented. I'd recommend including this with the 1.2 release (or is there a new recommended way to serialize Clojure data?) print-dup is likely for defrecord, but not deftype (roll your own there). Rich -- 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: Datatypes and Protocols update
On Apr 22, 6:53 pm, Rich Hickey richhic...@gmail.com wrote: [snip..] Feedback and errata welcome as always, 1) Typo on http://clojure.org/protocols: Section Basics defprotocol will automatically generate a corresponding interface, with the same name as the protocol, i.e. given a protocol my.ns/ Protocol, an interface my.ns.MyProtocol. my.ns.MyProtocol = my.ns.Protocol 2) Question. Could someone elaborate on the bullet You can implement a protocol on an interface Spefically the relation to multiple inheritance 3) When is 1.2 expected (roughly)? Thanks, /Karl [snip..] -- 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: Datatypes and Protocols update
On 23.04.2010, at 17:49, ataggart wrote: One problem with requiring the other object to be of the same type is that it would break the current model, e.g.: user= (= '(1 2 3) [1 2 3]) true I'd want this only for defrecord, which is a more limited (but also more convenient) way to define types. For the more basic and general deftype, everything should be possible. However, a perhaps better way to arrive at the same goal is to provide a macro that takes care of the type comparison. I'm left to wonder if it the more correct implementation of the desired behavior is not to override the equals, but rather to implement Comparable. Comparable requires a fully defined order relation, if I remember correctly. And it's not used by the standard Clojure comparison operators. Konrad. -- 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: Datatypes and Protocols update
On 23.04.2010, at 19:20, Rich Hickey wrote: You can get that back easily by implementing ILookup/IKeywordLookup, as does defrecord. I still have ideas about macro-like mixins for use in deftype, but I concluded the mandatory use of such mixins to create record-like things was too much user effort, and we need more experience with code reuse in deftype to determine need. That sounds very interesting. I have been playing with various ideas for reusing method code in several defttypes, but I am not really happy with any of them. Macros are difficult to use because nothing at the toplevel of a deftype form is macro-expanded. Syntax-quote is messy because it requires so many symbols to be prefixed with ~'. I ended up writing my own little templating system but its use is getting messier all the time as well. Konrad. -- 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: Datatypes and Protocols update
A few meandering observations: I like the latest change to include a this argument. It makes the number of arguments line up which is a good thing. I like the idea of defrecord for the common case, rather than having to request default implementations of various interfaces within deftype. Still, I think the splitting of deftype and defrecord has merely delayed, not eliminated, the need to eventually find a convenient, general scheme for layering in default implementations of interfaces. I look forward to seeing how this evolves. I like Rich's description of Clojure's datatypes/protocols as opinionated, and mostly agree with his manifesto. The one opinion I have the most difficulty with is: It has always been an unfortunate characteristic of using classes for application domain information that it resulted in information being hidden behind class-specific micro-languages, e.g. even the seemingly harmless employee.getName() is a custom interface to data. Putting information in such classes is a problem, much like having every book being written in a different language would be a problem. You can no longer take a generic approach to information processing. This results in an explosion of needless specificity, and a dearth of reuse. OO programmers write employee.getName() methods to preserve the option of doing something more sophisticated later, which isn't readily possible once all your clients start using employee.name everywhere. So if you really want to create a generic approach to information processing, it seems like the best approach is to address the underlying reason that these custom interfaces are needed. For this reason, I've always found appealing languages which let you optionally write getter/setter methods that hook into the standard field access syntax. This lets you start out with your fields public, and let your clients use the standard field access interface. Later, if you realize you need to do something special, you can easily add a custom getter without breaking your clients. As far as I know, Clojure doesn't currently make any attempt to address this problem of allowing a standard way to access public data from an object, while preserving the option of doing something more sophisticated later. So a programmer is still forced to choose between the convenience of keyword lookup of data, versus a protocol filled with get-name functions to preserve future flexibility. Ideally, I'd like to see a way to allow me to write a program using (:name employee), and later, if I need to, customize the employee datatype so that (:name employee) actually dispatches to some other function. Ditto with (assoc employee :name Mark) (for example, to validate the data in some way?). If I'm off-base, and these issues are easy to currently workaround in Clojure, I'd love to hear more about how others are handling this in their own programs. -- 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: Datatypes and Protocols update
On 24 April 2010 05:48, Mark Engelberg mark.engelb...@gmail.com wrote: Ideally, I'd like to see a way to allow me to write a program using (:name employee), and later, if I need to, customize the employee datatype so that (:name employee) actually dispatches to some other function. Ditto with (assoc employee :name Mark) (for example, to validate the data in some way?). You can do that with deftype by implementing the appropriate interfaces, like Rich mentioned in response to Konrad above. I've been playing around with this actually: http://gist.github.com/377480 Note that (:x (Foo. 1 2 3) nil) returns 1, while ((Foo. 1 2 3) :x) returns 2. (It could be the other way around or whatever, anything seems possible.) Also note that for some reason (:x (Foo. 1 2 3) nil) works fine, while (:x (Foo. 1 2 3)) throws an exception; not sure if I've run into a bug here or simply done something silly (I'd expect the latter though). Sincerely, Michał -- 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: Datatypes and Protocols update
I tried using deftype relatively recently, but realized it wouldn't work for my needs because serialization via *print-dup* wasn't yet implemented. I'd recommend including this with the 1.2 release (or is there a new recommended way to serialize Clojure data?) -- 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: Datatypes and Protocols update
On 22.04.2010, at 18:53, Rich Hickey wrote: Feedback and errata welcome as always, One feature in the deftype/defrecord split that I regret is that defrecord no longer allows the redefinition of equals and hashCode. Any attempt to override those results in an error message about duplicate method definitions. I have several former deftypes that are a perfect fit for the new defrecord, except that they need a specific comparison function. This is usually for excluding some fields from equality testing, or for requiring identity rather than equality for some fields. As it is, I must convert all these to the new deftype, losing the convient field access through keywords. Konrad. -- 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: Datatypes and Protocols update
On protocols: - doc string coming after the arg vecs seems odd. I'm used to putting them after the name of whatever I'm working on. On protocols doc: - You can implement a protocol on nil ... Object: could you elaborate on how these work and/or provide examples? I think this will solve the one problem I was running into earlier. - given a protocol my.ns/Protocol, an interface my.ns.MyProtocol: the final segments don't match. On Apr 22, 9:53 am, Rich Hickey richhic...@gmail.com wrote: I have been doing some work cleaning up the design and implementation of datatypes and protocols in preparation for the 1.2 release. Some notable changes for those who have been working with the earlier versions: deftype/reify now take an explicit 'this' argument in method sigs. The :as option is gone. There is a new datatype construct, defrecord. defrecord includes the implementation of persistent map, and the IPersistentMap magic of deftype is gone. deftype and defrecord create named classes, even in non-AOT use. There is no factory fn created, instead you can just call the constructor. The types for defrecord, deftype and definterface are automatically imported into the defining namespace. Substantial performance improvements to extend, and to higher-order use of protocol fns. In addition, I have started documenting these on the clojure.org site, and you should use this documentation instead of the wiki design docs, which will not be maintained. http://clojure.org/protocolshttp://clojure.org/datatypes Please note that these are just higher-level descriptions, and contain links to the detailed function docs, which are a must-read prior to use. Feedback and errata welcome as always, Rich -- 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 athttp://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: Datatypes and Protocols update
A good place to look for examples is protocols.clj and gvec.clj in clojure itself. protocols.clj includes an example of implementing a protocol on nil. Stu On protocols: - doc string coming after the arg vecs seems odd. I'm used to putting them after the name of whatever I'm working on. On protocols doc: - You can implement a protocol on nil ... Object: could you elaborate on how these work and/or provide examples? I think this will solve the one problem I was running into earlier. - given a protocol my.ns/Protocol, an interface my.ns.MyProtocol: the final segments don't match. On Apr 22, 9:53 am, Rich Hickey richhic...@gmail.com wrote: I have been doing some work cleaning up the design and implementation of datatypes and protocols in preparation for the 1.2 release. Some notable changes for those who have been working with the earlier versions: deftype/reify now take an explicit 'this' argument in method sigs. The :as option is gone. There is a new datatype construct, defrecord. defrecord includes the implementation of persistent map, and the IPersistentMap magic of deftype is gone. deftype and defrecord create named classes, even in non-AOT use. There is no factory fn created, instead you can just call the constructor. The types for defrecord, deftype and definterface are automatically imported into the defining namespace. Substantial performance improvements to extend, and to higher-order use of protocol fns. In addition, I have started documenting these on the clojure.org site, and you should use this documentation instead of the wiki design docs, which will not be maintained. http://clojure.org/protocolshttp://clojure.org/datatypes Please note that these are just higher-level descriptions, and contain links to the detailed function docs, which are a must-read prior to use. Feedback and errata welcome as always, Rich -- 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 athttp://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 -- 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: Datatypes and Protocols update
+1, I am also using this feature of the old deftype. On Apr 22, 12:15 pm, Konrad Hinsen konrad.hin...@fastmail.net wrote: On 22.04.2010, at 18:53, Rich Hickey wrote: Feedback and errata welcome as always, One feature in the deftype/defrecord split that I regret is that defrecord no longer allows the redefinition of equals and hashCode. Any attempt to override those results in an error message about duplicate method definitions. I have several former deftypes that are a perfect fit for the new defrecord, except that they need a specific comparison function. This is usually for excluding some fields from equality testing, or for requiring identity rather than equality for some fields. As it is, I must convert all these to the new deftype, losing the convient field access through keywords. Konrad. -- You received this message because you are subscribed to the Google Groups Clojure group.To post to this group, send email tocloj...@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email toclojure+unsubscr...@googlegroups.com For more options, visit this group athttp://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: Datatypes and Protocols update
Ah, great! And of course the piece I as missing is that nil and Object get supported via extend. Makes sense now given that that was the section of the doc, but it didn't click the first time through. On Apr 22, 2:54 pm, Stuart Halloway stuart.hallo...@gmail.com wrote: A good place to look for examples is protocols.clj and gvec.clj in clojure itself. protocols.clj includes an example of implementing a protocol on nil. Stu On protocols: - doc string coming after the arg vecs seems odd. I'm used to putting them after the name of whatever I'm working on. On protocols doc: - You can implement a protocol on nil ... Object: could you elaborate on how these work and/or provide examples? I think this will solve the one problem I was running into earlier. - given a protocol my.ns/Protocol, an interface my.ns.MyProtocol: the final segments don't match. On Apr 22, 9:53 am, Rich Hickey richhic...@gmail.com wrote: I have been doing some work cleaning up the design and implementation of datatypes and protocols in preparation for the 1.2 release. Some notable changes for those who have been working with the earlier versions: deftype/reify now take an explicit 'this' argument in method sigs. The :as option is gone. There is a new datatype construct, defrecord. defrecord includes the implementation of persistent map, and the IPersistentMap magic of deftype is gone. deftype and defrecord create named classes, even in non-AOT use. There is no factory fn created, instead you can just call the constructor. The types for defrecord, deftype and definterface are automatically imported into the defining namespace. Substantial performance improvements to extend, and to higher-order use of protocol fns. In addition, I have started documenting these on the clojure.org site, and you should use this documentation instead of the wiki design docs, which will not be maintained. http://clojure.org/protocolshttp://clojure.org/datatypes Please note that these are just higher-level descriptions, and contain links to the detailed function docs, which are a must-read prior to use. Feedback and errata welcome as always, Rich -- 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 athttp://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 -- 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 athttp://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: Datatypes and Protocols update
Minor errata barely worth mentioning:on the page: http://clojure.org/datatypes employeee.getName() employeee needs just 2 'e' characters. Cheers. -- 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: Datatypes and protocols - update
On Dec 11, 11:56 pm, ataggart alex.tagg...@gmail.com wrote: On Dec 11, 11:44 pm, ataggart alex.tagg...@gmail.com wrote: On Dec 11, 4:14 pm, Jason Wolfe jawo...@berkeley.edu wrote: I've been trying out the new branch, and on the whole I like it a lot. I know it'll take some time to learn how do things properly the new way, and I've figured out how to do most of the things I want to do thus far. Thanks, Rich! One thing I haven't figured out how to do cleanly without inheritance is to specify properties of objects in a hierarchical domain in a clean, efficient way. I'm sure I haven't fully wrapped my head around the new abstractions, so I'd love to hear about a clean way to solve this problem. A very simple example is: I have a protocol A, and sub-protocols A1 and A2. Every A is either an A1 or A2, but not both (and this split is closed, as far as I'm concerned). Sometimes I want to deal with instances of A1 and A2 together, and so I put the methods shared between all As in protocol A. But, at some point I need to separate out the A1s from the A2s. To do this, it seems like I have at least three options: 1. Add an is-A1 method to Protocol A. The problem with this option is that every type that derives from A1 needs to manually write out this method returning true, and vice-versa for implementers of A2. Users could eliminate this by extending their types with a mixin map to A, rather than implementing it directly in the deftype. But, this sacrifices readability (IMO) as well as efficiency. 2. use (satisfies? A1 x) to determine if x satisfies A1. The main problem with this, at least currently, is that satisfies? seems to be really slow in the negative case. I profiled my (non-trivial) program and half the runtime was going to reflection in satisfies? Moreover, this solution is not as general. 3. Use a multimethod. This would work generally and be reasonably efficient, but I feel like I'd be cluttering up my interface by mixing up protocols and multimethods. On the other hand, I guess multimethods are the main (only?) hierarchical construct built into Clojure, so maybe this is what's intended. So, which do people feel is preferred? Or have I missed a better option? Thanks! Jason If I understand Rich's reasoning, what you want runs antithetical to the protocols design, namely it being explicitly non-hierarchical. As such, you'd instead have 3 composable protocols, A, B, and C (where B and C would correspond to the functions of A1 and A2, respectively): (defprotocol A (a [x])) (defprotocol B (b [x])) (defprotocol C (c [x])) And then the type would reify the appropriate protocols: (deftype A1 [] A B (a [] (println in A1.a)) (b [] (println in A1.b))) (deftype A2 [] A C (a [] (println in A2.a)) (c [] (println in A2.c))) I should also note that isa? can be used for differentiation: user= (def my-a (A1)) #'user/my-a user= (isa? (type my-a) ::A1) true user= (isa? (type my-a) ::A2) false Oh, and it occurs to me that you could create an after-the-fact hierarchical relationship: user= (derive ::A1 ::A) nil user= (derive ::A2 ::A) nil user= (isa? (type my-a) ::A) true -- 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: Datatypes and protocols - update
On 12 Dec 2009, at 01:14, Jason Wolfe wrote: A very simple example is: I have a protocol A, and sub-protocols A1 and A2. Every A is either an A1 or A2, but not both (and this split is closed, as far as I'm concerned). Sometimes I want to deal with instances of A1 and A2 together, and so I put the methods shared between all As in protocol A. But, at some point I need to separate out the A1s from the A2s. To do this, it seems like I have at least three options: Those are the options you have in the world of protocols, types/ classes, and hierarchies. But there are many more options for classifying objects. For example, you could have a set A1 to which you add all the types in your A1 category. Or you could call functions specific to A1 or A2 through a lookup table implemented as a map. I can't judge if any of these would work fine for you, of course. Konrad. -- 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: Datatypes and protocols - update
On Dec 11, 8:48 am, Chris Kent cjk...@gmail.com wrote: Rich Hickey richhickey at gmail.com writes: An updated version of the code for datatypes[1] and protocols[2] is now available in the 'new' branch[3]. I've converted some code that used gen-class to use deftype and defprotocol and the results are great so far. The code is shorter, easier to write and the intent is much clearer. I'm a big fan. I've come across one problem though. I've created a type with deftype that calls a function in one of its methods: (ns ns1.deftypetest) (defn bar [] bar) (defprotocol P (foo [p])) (deftype T [] P (foo [] (bar))) When I compile the namespace, create an instance of the type from Java and invoke the foo method I get: java.lang.IllegalStateException: Var ns1.deftypetest/bar is unbound. I guess the namespace isn't getting loaded. Should this work? I've created a class in the same namespace using gen-class and that has no problem invoking the function when it's instantiated from Java. Currently deftype classes do no automatic namespace loading, unlike gen-class classes. The difference is gen-class classes are AOT-only, whereas deftype classes can be defined dynamically, where reloading namespaces would not be desired. I'm still thinking about how best to support Java consumption of deftype classes. For now, you will need some init glue code to load any support namespaces. Rich -- 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: Datatypes and protocols - update
Rich Hickey richhickey at gmail.com writes: An updated version of the code for datatypes[1] and protocols[2] is now available in the 'new' branch[3]. I've converted some code that used gen-class to use deftype and defprotocol and the results are great so far. The code is shorter, easier to write and the intent is much clearer. I'm a big fan. I've come across one problem though. I've created a type with deftype that calls a function in one of its methods: (ns ns1.deftypetest) (defn bar [] bar) (defprotocol P (foo [p])) (deftype T [] P (foo [] (bar))) When I compile the namespace, create an instance of the type from Java and invoke the foo method I get: java.lang.IllegalStateException: Var ns1.deftypetest/bar is unbound. I guess the namespace isn't getting loaded. Should this work? I've created a class in the same namespace using gen-class and that has no problem invoking the function when it's instantiated from Java. Thanks Chris -- 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: Datatypes and protocols - update
I've been trying out the new branch, and on the whole I like it a lot. I know it'll take some time to learn how do things properly the new way, and I've figured out how to do most of the things I want to do thus far. Thanks, Rich! One thing I haven't figured out how to do cleanly without inheritance is to specify properties of objects in a hierarchical domain in a clean, efficient way. I'm sure I haven't fully wrapped my head around the new abstractions, so I'd love to hear about a clean way to solve this problem. A very simple example is: I have a protocol A, and sub-protocols A1 and A2. Every A is either an A1 or A2, but not both (and this split is closed, as far as I'm concerned). Sometimes I want to deal with instances of A1 and A2 together, and so I put the methods shared between all As in protocol A. But, at some point I need to separate out the A1s from the A2s. To do this, it seems like I have at least three options: 1. Add an is-A1 method to Protocol A. The problem with this option is that every type that derives from A1 needs to manually write out this method returning true, and vice-versa for implementers of A2. Users could eliminate this by extending their types with a mixin map to A, rather than implementing it directly in the deftype. But, this sacrifices readability (IMO) as well as efficiency. 2. use (satisfies? A1 x) to determine if x satisfies A1. The main problem with this, at least currently, is that satisfies? seems to be really slow in the negative case. I profiled my (non-trivial) program and half the runtime was going to reflection in satisfies? Moreover, this solution is not as general. 3. Use a multimethod. This would work generally and be reasonably efficient, but I feel like I'd be cluttering up my interface by mixing up protocols and multimethods. On the other hand, I guess multimethods are the main (only?) hierarchical construct built into Clojure, so maybe this is what's intended. So, which do people feel is preferred? Or have I missed a better option? Thanks! Jason -- 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: Datatypes and protocols - update
On Dec 11, 4:14 pm, Jason Wolfe jawo...@berkeley.edu wrote: I've been trying out the new branch, and on the whole I like it a lot. I know it'll take some time to learn how do things properly the new way, and I've figured out how to do most of the things I want to do thus far. Thanks, Rich! One thing I haven't figured out how to do cleanly without inheritance is to specify properties of objects in a hierarchical domain in a clean, efficient way. I'm sure I haven't fully wrapped my head around the new abstractions, so I'd love to hear about a clean way to solve this problem. A very simple example is: I have a protocol A, and sub-protocols A1 and A2. Every A is either an A1 or A2, but not both (and this split is closed, as far as I'm concerned). Sometimes I want to deal with instances of A1 and A2 together, and so I put the methods shared between all As in protocol A. But, at some point I need to separate out the A1s from the A2s. To do this, it seems like I have at least three options: 1. Add an is-A1 method to Protocol A. The problem with this option is that every type that derives from A1 needs to manually write out this method returning true, and vice-versa for implementers of A2. Users could eliminate this by extending their types with a mixin map to A, rather than implementing it directly in the deftype. But, this sacrifices readability (IMO) as well as efficiency. 2. use (satisfies? A1 x) to determine if x satisfies A1. The main problem with this, at least currently, is that satisfies? seems to be really slow in the negative case. I profiled my (non-trivial) program and half the runtime was going to reflection in satisfies? Moreover, this solution is not as general. 3. Use a multimethod. This would work generally and be reasonably efficient, but I feel like I'd be cluttering up my interface by mixing up protocols and multimethods. On the other hand, I guess multimethods are the main (only?) hierarchical construct built into Clojure, so maybe this is what's intended. So, which do people feel is preferred? Or have I missed a better option? Thanks! Jason If I understand Rich's reasoning, what you want runs antithetical to the protocols design, namely it being explicitly non-hierarchical. As such, you'd instead have 3 composable protocols, A, B, and C (where B and C would correspond to the functions of A1 and A2, respectively): (defprotocol A (a [x])) (defprotocol B (b [x])) (defprotocol C (c [x])) And then the type would reify the appropriate protocols: (deftype A1 [] A B (a [] (println in A1.a)) (b [] (println in A1.b))) (deftype A2 [] A C (a [] (println in A2.a)) (c [] (println in A2.c))) -- 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: Datatypes and protocols - update
On Dec 11, 11:44 pm, ataggart alex.tagg...@gmail.com wrote: On Dec 11, 4:14 pm, Jason Wolfe jawo...@berkeley.edu wrote: I've been trying out the new branch, and on the whole I like it a lot. I know it'll take some time to learn how do things properly the new way, and I've figured out how to do most of the things I want to do thus far. Thanks, Rich! One thing I haven't figured out how to do cleanly without inheritance is to specify properties of objects in a hierarchical domain in a clean, efficient way. I'm sure I haven't fully wrapped my head around the new abstractions, so I'd love to hear about a clean way to solve this problem. A very simple example is: I have a protocol A, and sub-protocols A1 and A2. Every A is either an A1 or A2, but not both (and this split is closed, as far as I'm concerned). Sometimes I want to deal with instances of A1 and A2 together, and so I put the methods shared between all As in protocol A. But, at some point I need to separate out the A1s from the A2s. To do this, it seems like I have at least three options: 1. Add an is-A1 method to Protocol A. The problem with this option is that every type that derives from A1 needs to manually write out this method returning true, and vice-versa for implementers of A2. Users could eliminate this by extending their types with a mixin map to A, rather than implementing it directly in the deftype. But, this sacrifices readability (IMO) as well as efficiency. 2. use (satisfies? A1 x) to determine if x satisfies A1. The main problem with this, at least currently, is that satisfies? seems to be really slow in the negative case. I profiled my (non-trivial) program and half the runtime was going to reflection in satisfies? Moreover, this solution is not as general. 3. Use a multimethod. This would work generally and be reasonably efficient, but I feel like I'd be cluttering up my interface by mixing up protocols and multimethods. On the other hand, I guess multimethods are the main (only?) hierarchical construct built into Clojure, so maybe this is what's intended. So, which do people feel is preferred? Or have I missed a better option? Thanks! Jason If I understand Rich's reasoning, what you want runs antithetical to the protocols design, namely it being explicitly non-hierarchical. As such, you'd instead have 3 composable protocols, A, B, and C (where B and C would correspond to the functions of A1 and A2, respectively): (defprotocol A (a [x])) (defprotocol B (b [x])) (defprotocol C (c [x])) And then the type would reify the appropriate protocols: (deftype A1 [] A B (a [] (println in A1.a)) (b [] (println in A1.b))) (deftype A2 [] A C (a [] (println in A2.a)) (c [] (println in A2.c))) I should also note that isa? can be used for differentiation: user= (def my-a (A1)) #'user/my-a user= (isa? (type my-a) ::A1) true user= (isa? (type my-a) ::A2) false -- 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: Datatypes and protocols - update
On Mon, 07 Dec 2009 12:07:12 -0500, Laurent PETIT laurent.pe...@gmail.com wrote: 2009/12/7 Hugo Duncan hugodun...@users.sourceforge.net On Mon, 07 Dec 2009 06:53:38 -0500, Rich Hickey richhic...@gmail.com wrote: Yes, methods are not really functions. Thinking about them as closures over the object is a good way to go - you can see that analogy in play when you consider recur, which works with these methods, but could not rebind 'this'. The recur case sealed the deal in the decision not to include 'this' in the argument lists. I had a quick play with protocols, and the biggest problem I had getting started was realising that the signature of a method definition in defprotocol was different to the signature required to implement the same method in deftype. FWIW, I found it very non-intuitive. And now that you've got it, do you still feel this non-intuitive. Because I had the same feeling first: I thought I would never rembember how things work and why put 'this-like args there, and not there ... But now that everything clicked in place, I feel the last status of what Rich achieved to do the most natural and intuitive. I'll no doubt get used to it :-) A couple of things that would have helped me get it: From the deftype doc: Thus methods for protocols will take one fewer arguments than do the protocol functions. would (at least for me) be clearer as: Thus methods for protocols are implemented with one fewer argument than in the protocol function definitions. The example of a deftype protocol implementation that is in the defprotocol doc string could be repeated in the deftype doc string. Basically, what helped me was along the lines of what Konrad said : * defprotocol and extend are purely functional : so you have to specify every argument, including the object the functions acts upon. * deftype with embedded protocol definition for the type, or reify, in the contrary, do not define pure functions. They define methods. You cannot get them as values and pass them around like higher-order functions, for example. And you must know this fact, it cannot be an implementation detail. So, since you know this fact, you remember that you're in a method definition (in the general sense of object oriented languages : method of a class) and, as you do with e.g. java, C#, ... , when definining methods, you do not add the target of the method as an implicit argument. The big advantage I see to this is that once you get it, you don't have anymore to remember where 'this is explicit and where it's implicit: it's intuitive. The other big advantage is that the use of recur inside these functions/methods bodies continue to match exactly the signature of the function/method (otherwise you would have had to remember that, e.g. in methods defined via deftype, you must place an explicit this argument in the method arg list, but not place it in the recur calls ... :-( ) That explanation certainly helps describe and clarify the reasons for the difference. I still find it counter-intuitive that the definition follows the syntax of the functional world, and the implementation that of an object orientated world. However, I can't think of any suggestion to resolve this, and as you say it does reflect the reality of the situation, so I'll get used to it :-) -- Hugo Duncan -- 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: Datatypes and protocols - update
On 01.12.2009, at 02:42, Rich Hickey wrote: An updated version of the code for datatypes[1] and protocols[2] is now available in the 'new' branch[3]. This weekend I finally got around to converting all my deftype-and- defprotocol-using code to the current Clojure new branch. It is now more compact and more readable, and the few parts where performance matters are faster. A big step forward! Small changes include: - No more use of . in reify/deftype method names - No more implicit this in reify/deftype That's the only feature that I regret a bit. It looks weird in a functional language to have functions (or something that is very similar) that have an implicit argument named outside its definition (in the :as option). Of course, one can think of it as similar to a closure over the object, but it still looks a bit weird to me. Konrad. -- 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: Datatypes and protocols - update
On Mon, Dec 7, 2009 at 3:11 AM, Konrad Hinsen konrad.hin...@fastmail.net wrote: On 01.12.2009, at 02:42, Rich Hickey wrote: An updated version of the code for datatypes[1] and protocols[2] is now available in the 'new' branch[3]. This weekend I finally got around to converting all my deftype-and- defprotocol-using code to the current Clojure new branch. It is now more compact and more readable, and the few parts where performance matters are faster. A big step forward! Small changes include: - No more use of . in reify/deftype method names - No more implicit this in reify/deftype That's the only feature that I regret a bit. It looks weird in a functional language to have functions (or something that is very similar) that have an implicit argument named outside its definition (in the :as option). Of course, one can think of it as similar to a closure over the object, but it still looks a bit weird to me. Yes, methods are not really functions. Thinking about them as closures over the object is a good way to go - you can see that analogy in play when you consider recur, which works with these methods, but could not rebind 'this'. The recur case sealed the deal in the decision not to include 'this' in the argument lists. Rich -- 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: Datatypes and protocols - update
On Mon, 07 Dec 2009 06:53:38 -0500, Rich Hickey richhic...@gmail.com wrote: Yes, methods are not really functions. Thinking about them as closures over the object is a good way to go - you can see that analogy in play when you consider recur, which works with these methods, but could not rebind 'this'. The recur case sealed the deal in the decision not to include 'this' in the argument lists. I had a quick play with protocols, and the biggest problem I had getting started was realising that the signature of a method definition in defprotocol was different to the signature required to implement the same method in deftype. FWIW, I found it very non-intuitive. -- Hugo Duncan -- 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: Datatypes and protocols - update
2009/12/7 Hugo Duncan hugodun...@users.sourceforge.net On Mon, 07 Dec 2009 06:53:38 -0500, Rich Hickey richhic...@gmail.com wrote: Yes, methods are not really functions. Thinking about them as closures over the object is a good way to go - you can see that analogy in play when you consider recur, which works with these methods, but could not rebind 'this'. The recur case sealed the deal in the decision not to include 'this' in the argument lists. I had a quick play with protocols, and the biggest problem I had getting started was realising that the signature of a method definition in defprotocol was different to the signature required to implement the same method in deftype. FWIW, I found it very non-intuitive. Hello, And now that you've got it, do you still feel this non-intuitive. Because I had the same feeling first: I thought I would never rembember how things work and why put 'this-like args there, and not there ... But now that everything clicked in place, I feel the last status of what Rich achieved to do the most natural and intuitive. Basically, what helped me was along the lines of what Konrad said : * defprotocol and extend are purely functional : so you have to specify every argument, including the object the functions acts upon. * deftype with embedded protocol definition for the type, or reify, in the contrary, do not define pure functions. They define methods. You cannot get them as values and pass them around like higher-order functions, for example. And you must know this fact, it cannot be an implementation detail. So, since you know this fact, you remember that you're in a method definition (in the general sense of object oriented languages : method of a class) and, as you do with e.g. java, C#, ... , when definining methods, you do not add the target of the method as an implicit argument. The big advantage I see to this is that once you get it, you don't have anymore to remember where 'this is explicit and where it's implicit: it's intuitive. The other big advantage is that the use of recur inside these functions/methods bodies continue to match exactly the signature of the function/method (otherwise you would have had to remember that, e.g. in methods defined via deftype, you must place an explicit this argument in the method arg list, but not place it in the recur calls ... :-( ) HTH, -- laurent -- 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: Datatypes and protocols - update
On Dec 7, 9:07 am, Laurent PETIT laurent.pe...@gmail.com wrote: 2009/12/7 Hugo Duncan hugodun...@users.sourceforge.net On Mon, 07 Dec 2009 06:53:38 -0500, Rich Hickey richhic...@gmail.com wrote: Yes, methods are not really functions. Thinking about them as closures over the object is a good way to go - you can see that analogy in play when you consider recur, which works with these methods, but could not rebind 'this'. The recur case sealed the deal in the decision not to include 'this' in the argument lists. I had a quick play with protocols, and the biggest problem I had getting started was realising that the signature of a method definition in defprotocol was different to the signature required to implement the same method in deftype. FWIW, I found it very non-intuitive. Hello, And now that you've got it, do you still feel this non-intuitive. Because I had the same feeling first: I thought I would never rembember how things work and why put 'this-like args there, and not there ... But now that everything clicked in place, I feel the last status of what Rich achieved to do the most natural and intuitive. Basically, what helped me was along the lines of what Konrad said : * defprotocol and extend are purely functional : so you have to specify every argument, including the object the functions acts upon. * deftype with embedded protocol definition for the type, or reify, in the contrary, do not define pure functions. They define methods. You cannot get them as values and pass them around like higher-order functions, for example. And you must know this fact, it cannot be an implementation detail. So, since you know this fact, you remember that you're in a method definition (in the general sense of object oriented languages : method of a class) and, as you do with e.g. java, C#, ... , when definining methods, you do not add the target of the method as an implicit argument. The big advantage I see to this is that once you get it, you don't have anymore to remember where 'this is explicit and where it's implicit: it's intuitive. The other big advantage is that the use of recur inside these functions/methods bodies continue to match exactly the signature of the function/method (otherwise you would have had to remember that, e.g. in methods defined via deftype, you must place an explicit this argument in the method arg list, but not place it in the recur calls ... :-( ) HTH, -- laurent That was my experience as well. It started off as a gotcha (since I was copy/pasting the protocol definitions over to the deftype), but then after playing for a bit it wasn't a big deal. In all my usages so far I haven't needed to reference 'this'. The one area I am running into issues is with being able to provide a default implementation, or extending types such that I can override a method. For example, I have: (defprotocol http-resource (GET [res req resp]) (POST[res req resp]) (PUT [res req resp]) (DELETE [res req resp]) (HEAD[res req resp]) (OPTIONS [res req resp]) (TRACE [res req resp])) But I considering the usage, most of those need not be implemented per- type, and could all be defaulted to something like: (deftype resource [] http-resource (GET [req resp] (send-status! resp 405)) (POST [req resp] (send-status! resp 405)) (PUT [req resp] (send-status! resp 405)) (DELETE [req resp] (send-status! resp 405)) (HEAD [req resp] (send-status! resp 405)) (OPTIONS [req resp] (send-status! resp 405)) (TRACE [req resp] (send-status! resp 405))) Alas I can't simply extend-type since that modifies the type, instead of creating a new, modified type. And even then, methods in the extension map don't get called if the method exists directly on the type, i.e., no overriding. I'm sure my problem is simply vestigial OO thinking, but I'm not sure how to achieve the simplicity I want. The one route I tried that sort- of works is making a macro to create the types, rather than extending some extant implementation. The downside is I have to see which methods I'm being given and only provide defaults for the method names/arities that aren't. It might be sufficient if there was some facility for cloning another type and overriding certain methods, though I can foresee problems dealing with managing the field definitions between the original and the altered clone. -- 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: Datatypes and protocols - update
On Dec 7, 11:23 am, ataggart alex.tagg...@gmail.com wrote: On Dec 7, 9:07 am, Laurent PETIT laurent.pe...@gmail.com wrote: 2009/12/7 Hugo Duncan hugodun...@users.sourceforge.net On Mon, 07 Dec 2009 06:53:38 -0500, Rich Hickey richhic...@gmail.com wrote: Yes, methods are not really functions. Thinking about them as closures over the object is a good way to go - you can see that analogy in play when you consider recur, which works with these methods, but could not rebind 'this'. The recur case sealed the deal in the decision not to include 'this' in the argument lists. I had a quick play with protocols, and the biggest problem I had getting started was realising that the signature of a method definition in defprotocol was different to the signature required to implement the same method in deftype. FWIW, I found it very non-intuitive. Hello, And now that you've got it, do you still feel this non-intuitive. Because I had the same feeling first: I thought I would never rembember how things work and why put 'this-like args there, and not there ... But now that everything clicked in place, I feel the last status of what Rich achieved to do the most natural and intuitive. Basically, what helped me was along the lines of what Konrad said : * defprotocol and extend are purely functional : so you have to specify every argument, including the object the functions acts upon. * deftype with embedded protocol definition for the type, or reify, in the contrary, do not define pure functions. They define methods. You cannot get them as values and pass them around like higher-order functions, for example. And you must know this fact, it cannot be an implementation detail. So, since you know this fact, you remember that you're in a method definition (in the general sense of object oriented languages : method of a class) and, as you do with e.g. java, C#, ... , when definining methods, you do not add the target of the method as an implicit argument. The big advantage I see to this is that once you get it, you don't have anymore to remember where 'this is explicit and where it's implicit: it's intuitive. The other big advantage is that the use of recur inside these functions/methods bodies continue to match exactly the signature of the function/method (otherwise you would have had to remember that, e.g. in methods defined via deftype, you must place an explicit this argument in the method arg list, but not place it in the recur calls ... :-( ) HTH, -- laurent That was my experience as well. It started off as a gotcha (since I was copy/pasting the protocol definitions over to the deftype), but then after playing for a bit it wasn't a big deal. In all my usages so far I haven't needed to reference 'this'. The one area I am running into issues is with being able to provide a default implementation, or extending types such that I can override a method. For example, I have: (defprotocol http-resource (GET [res req resp]) (POST [res req resp]) (PUT [res req resp]) (DELETE [res req resp]) (HEAD [res req resp]) (OPTIONS [res req resp]) (TRACE [res req resp])) But I considering the usage, most of those need not be implemented per- type, and could all be defaulted to something like: (deftype resource [] http-resource (GET [req resp] (send-status! resp 405)) (POST [req resp] (send-status! resp 405)) (PUT [req resp] (send-status! resp 405)) (DELETE [req resp] (send-status! resp 405)) (HEAD [req resp] (send-status! resp 405)) (OPTIONS [req resp] (send-status! resp 405)) (TRACE [req resp] (send-status! resp 405))) Alas I can't simply extend-type since that modifies the type, instead of creating a new, modified type. And even then, methods in the extension map don't get called if the method exists directly on the type, i.e., no overriding. I'm sure my problem is simply vestigial OO thinking, but I'm not sure how to achieve the simplicity I want. The one route I tried that sort- of works is making a macro to create the types, rather than extending some extant implementation. The downside is I have to see which methods I'm being given and only provide defaults for the method names/arities that aren't. It might be sufficient if there was some facility for cloning another type and overriding certain methods, though I can foresee problems dealing with managing the field definitions between the original and the altered clone. One idea: (defdefault name options* specs*) Similar to deftype except without any field definitions (a simplifying restriction), thus can only operate on their args. With that I could do: (defdefault base-resource http-resource (GET [req resp] (send-status! resp 405)) (POST [req resp] (send-status! resp 405)) (PUT [req resp] (send-status! resp 405)) (DELETE [req resp] (send-status! resp 405)) (HEAD
Re: Datatypes and protocols - update
On Dec 1, 9:56 pm, Rich Hickey richhic...@gmail.com wrote: There are 2 ways to make a deftype reach a protocol. First, you can implement the protocol directly in the deftype/reify, supplying the protocol where you do interfaces, and the methods of the protocol as methods of the type. The type will be made to implement the protocol's interface. The second way, for types you don't control, is to use extend-type/class/protocol, which will create method maps and register them with the protocol. For the record; this means that you cannot implement two protocols directly in deftype or reify if those protocols have fns with the same names and signatures. You can, however, implement one protocol directly, and then extend it to the other, as with the following, rather silly, example: user (ns foo) nil foo (defprotocol Foo (write [this])) Foo foo (ns bar) nil bar (defprotocol Bar (write [this badger])) Bar bar (ns me) nil me (defprotocol Me (write [this] [this badger])) Me me (ns user) nil user (deftype FooMe [a b c d] foo/Foo (write [] a) bar/Bar (write [badger] b)) #'user/FooMe user (def fm (FooMe :foo :bar :me1 :me2)) #'user/fm user (foo/write fm) :foo user (bar/write fm 1) :bar user (extend-type ::FooMe me/Me (write ([this] (:c this)) ([this badger] (:d this nil user (me/write fm) :me1 user (me/write fm 1) :me2 user (foo/write fm) :foo user (bar/write fm 1) :bar user which was unexpectedly sweet, yet totally consistent with your explanation of how it works. Rich, you _are_ the Badgers Nadgers. -Dave -- 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: Datatypes and protocols - update
On Dec 2, 12:29 am, Krukow karl.kru...@gmail.com wrote: On Dec 1, 10:56 pm, Rich Hickey richhic...@gmail.com wrote: [snip] There are 2 ways to make a deftype reach a protocol. First, you can implement the protocol directly in the deftype/reify, supplying the protocol where you do interfaces, and the methods of the protocol as methods of the type. The type will be made to implement the protocol's interface. OK. With extend you can use maps and merge to share implementations. Does directly implementing the protocol in deftype allow also for abstract super-classes, i.e., sharing protocol-function implementations across types? Right now you would just call an implementation helper inside your method. I'm still considering if more support is needed, and what form it might take. Different methods of implementing the protocol have different performance. Implementing directly in deftype or reify is as fast as a direct interface call. Using extend-* is not quite as fast, but still fast. Both methods have direct support in callsites, so a call to a protocol fn has support both for using the interface and caching lookup results. Great. So the preferred way for data-types in my program is to implement the protocol directly, whereas for other types I can still use my protocol with extend. Just to confirm my understanding: Is it correct to say, for example, that clojure.lang.Seqable will be a protocol implemented directly in the Clojure data types, whereas it would reach the Java lang types using extend? Yes. extend is the key to removing the current (closed) multiway conditionals in, e.g., RT.seq/seqFrom, and is much faster as well. In Clojure-in-Java interfaces like IPersistentCollection extend Seqable: would these be unrelated type-wise as protocols? Yes. One of the reasons we use interface inheritance in a language like Java is that, short of generifying everything, we have no way to say: foo(Counted+Sorted+Seqable+Collection coll){...} We only get to specify one type, and any other interfaces it doesn't imply require casts. So we use hierarchy to reduce the required casting, but it has a cost in flexibility - i.e. you can't make a non- Seqable collection, if that made sense. In a dynamic language with protocols there is no reason to do it this way. You don't need to have a single type imply multiple types via hierarchy, and you don't need to declare anything. So each protocol is a la carte, and a piece of code that requires the collection support Counted, Sorted, Seqable and Collection protocols will simply use those protocols, and work with anything that supports them. A type can support any and just the protocols that make sense for it, without bringing in others as a side effect of hierarchy. Protocols are very much about polymorphism without hierarchy. Rich -- 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: Datatypes and protocols - update
Thanks for sharing the insights. /Karl -- 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: Datatypes and protocols - update
On Dec 1, 2:42 am, Rich Hickey richhic...@gmail.com wrote: I have done a lot of work on performance, and refined the design. The big news is that you can now directly implement a protocol inside a deftype, and you can also reify protocols. This cements protocols as the superior way to model the things for which they are suitable, since they can match the performance of interfaces without their limitations. First of all, I think this is a wonderful addition to the language. I've tried what is in the new branch on a small but real example, and I am quite happy with it: So thanks! Could you go into more detail about how protocols and datatypes are actually implemented, and the performance improvements you've recently made? (probably I'm not the only one interested :-) What is generated when I define a protocol, datatype and extend the type to the protocol? As I understand the performance of calling a protocol function matches the performance of calling an interface method in Java. How is it possible to achieve this in combination with the dynamic extensibility of extend? /Karl -- 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: Datatypes and protocols - update
On Dec 1, 10:56 pm, Rich Hickey richhic...@gmail.com wrote: [snip] There are 2 ways to make a deftype reach a protocol. First, you can implement the protocol directly in the deftype/reify, supplying the protocol where you do interfaces, and the methods of the protocol as methods of the type. The type will be made to implement the protocol's interface. OK. With extend you can use maps and merge to share implementations. Does directly implementing the protocol in deftype allow also for abstract super-classes, i.e., sharing protocol-function implementations across types? [snip] Different methods of implementing the protocol have different performance. Implementing directly in deftype or reify is as fast as a direct interface call. Using extend-* is not quite as fast, but still fast. Both methods have direct support in callsites, so a call to a protocol fn has support both for using the interface and caching lookup results. Great. So the preferred way for data-types in my program is to implement the protocol directly, whereas for other types I can still use my protocol with extend. Just to confirm my understanding: Is it correct to say, for example, that clojure.lang.Seqable will be a protocol implemented directly in the Clojure data types, whereas it would reach the Java lang types using extend? In Clojure-in-Java interfaces like IPersistentCollection extend Seqable: would these be unrelated type-wise as protocols? The most important thing is, writing to protocols gives you a dynamic, open, extensible system not tied to derivation, and is fast, so a great way to architect the polymorphic part of your designs (when single-dispatch is appropriate). Rich I think these constructs will have great impact on how we will structure our Clojure programs. /Karl -- 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