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

Reply via email to