Oskar <oskar.kv...@gmail.com> writes: > Why protocols (and records)? What benefits do I get? Alex mentioned > polymorphism; how is this different from/related to multimethods?
As Andreas mentioned, yes protocols basically give you a subset of the polymorphism functionality of multimethods. But they also give you some benefits that multimethods do not. Otherwise they would not have been added to the language. ;-) There are a number of motivations which Rich lists here: http://clojure.org/protocols http://clojure.org/datatypes I will try to cover the main ones with a bit of explanation. 1. Performance Multimethods are not fast enough for writing high-performance data structures in the computer science sense (hash tables, red-black trees etc). One of the motivations for adding types and protocols was that it should be possible to write Clojure's persistent data structures in an idiomatic, fast way in Clojure itself, rather than in Java. ClojureScript is in part a validation of this idea. The data structures and core library of ClojureScript are written completely in ClojureScript, not in JavaScript. In the ideal case protocols can map to native Java interfaces and so benefit from the JVMs optimizations. Of course protocols can do things which interfaces cannot (extend) so there is a gradual performance degradation if they are used in more dynamic ways. Similarly, records replace the earlier feature "structs", which were also high-performance record types. Structs were designed to be used with multimethods but unfortunately multimethods negated some of their performance benefits. 2. Abstraction and organization Rich lists the point "Support the 90% case of multimethods while providing higher-level abstraction/organization". Single dispatch on type is by far the most common use of polymorphism. This is why it is the only (native) polymorphism mechanism in many languages. Many use cases do not need the full power of multimethods. One thing that Java's interfaces can do that multimethods cannot is group a set of tightly related methods together. For example, let's look at the ISeq protocol from ClojureScript. (In Clojure ISeq is a Java interface rather than a protocol for historical reasons but the use case is the same.) (defprotocol ISeq (-first [coll]) (-rest [coll])) ISeq contains two methods: "-first" and "-rest". To have meaningful seq you really need both of them. A seq with a "rest" but no "first" would be pretty silly. By grouping these two methods into a single protocol you communicate to reader of your code they are tightly and indivisibly related and should be implemented together. This is not enforced of course, it's about communicating your intentions, not restricting what the programmer can do. 3. Host interoperability Where possible types and protocols map to similar concepts in Java's classes and interfaces. A type can implement a Java interface and a Java class can extend a Clojure protocol. The mapping is of course not perfect. For example you can't use "extend" on a Java interface as Java doesn't have this concept. Similarly you can't subclass a Java class with a Clojure type as by design Clojure types do not have a notion of inheritance. While they do not cover all interop needs they do provide a useful, more convenient and high performance replacement for some of the use cases of earlier interop mechanisms like proxy and gen-class. > Let's take the game example again. Say I have (which I do) a map of > characters in my game that contains both monsters and players. > > A monster: > {:type :mob :pos [x y] :hp 30 :max-hp 30 :dmg 5 :name "an orc > pawn" :attacking false :last-attack 0 :attack-delay 1500 :target > nil :path nil :speed 2.5 :ai ai/attack-nearest} > A player: > {:type :player :name "Bill" :pos [0 0] :hp 100 :max-hp 100 :dmg > 10 :attacking false :last-attack 0 :attack-delay 1500 :target nil} > > And I have a few things I want to do to them. Like attack, draw them, > take damage, die and respawn, etc. Right now all those things work > exactly the same way for both types. Partly because they are so > similar, but also because the game is not nearly done yet. For > example, I might want to draw them differently, or have them take > damage differently, or whatever. I use the type mostly to filter the > map, for example monsters can only attack players. > > Is it a good idea to use protocols (and records?) for me in this > situation (now or in the future when I might want them to behave > differently)? How can I benefit from them? You can of course use multimethods for these use cases and there is nothing wrong with doing so. However for those 90% of cases which protocols do cover you may find they are a more natural fit and give you some of the benefits I listed above. For the remaining 10% where you need the additional power of multimethods you can of course continue to use them. They're not going away. The main use cases for the old structs feature on the other hand are completely replaced by records and it is recommended that new code use defrecord instead of defstruct. -- 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