Flowchart for choosing the right Clojure type definition form

Clojure offers a number of different forms that define types (and generate Java classes). Choosing between deftype, defrecord, reify, proxy, and gen-class can be a tripping point for those new to Clojure.

I’d obviously like to make such decisions easier for everyone.  I know that many people learn best through visual aids, so I’ve been working on a flowchart that attempts to encapsulate some of the significant choices that go into deciding between the different type-definition forms.  A draft of it is below; let me know if it is helpful to you (or not!), how you think it could be made better, etc.  The most recent version of the flowchart, the “source” OmniGraffle file, and many translations of it (feel free to contribute another one!) will always be available here.

“The ‘Interop Zone'” demarcates use cases (e.g. needing to define multiple constructors) and forms (proxy and gen-class) that are exclusively the domain of Clojure’s JVM interoperability support. Using these forms is fine, but be aware that by doing so, you are stepping outside of Clojure’s “native” abstractions; unless you are defining a type for the express purpose of meeting interoperability requirements, one of Clojure’s simpler type-definition forms may suit your needs better.

Updated 2011-07-05 18:28 UTC based on initial feedback. Thanks!

Updated 2011-07-07 17:38 UTC — added “The ‘Interop Zone'”

Updated 2011-08-09 16:30 UTC — Japanese translation provided by OGINO Masanori

Updated 2011-08-23 17:33 UTC — Portuguese translation provided by Paulo Suzart

Final update: The most recent version of the flowchart, the “source” OmniGraffle file, and many translations of it (feel free to contribute another one!) will always be available here.

13 thoughts on “Flowchart for choosing the right Clojure type definition form

  1. This is very nice. One comment:

    The two parts of the bottom-right parallelogram don’t seem to connect to me. I can imagine thinking to myself: “Is this class modeling a domain value? No. Do I want to use (:field object) notation and maybe assoc new keys onto the object? Yes.”

    My gut reaction is that the default choice in this parallelogram is defrecord unless you want to explicitly prevent map functions like assoc from working on the type. I mean, even if you always use the (.field object) form and don’t assoc, etc., does it *hurt* to use a defrecord?

    1. Yes, absolutely. In particular, if you want to provide your own implementations for these things: suppose you’re defining a new type of IPersistentMap. Then you can’t afford defrecord’s built-ins, which would get in the way of your special versions.

  2. If this is meant to help newcomers then IMHO it is necessary to stress “use regular maps first”. Because a lot of people coming from rigid OO languages (java) would not even think about it.

  3. I notice one decision path problem.

    1. need to extend an existing Java class
    2. must be a named type
    3. does not need to be referred statically from Java

    …leads me to choose deftype, which is not possible given (1)

    1. That first decision isn’t exclusively indicating that you need to extend a class — it includes implementing interfaces. So, there is a bit of ambiguity there. Of course, you’re in gen-class-land there, but breaking things out more to eliminate the ambiguity would make the flowchart a fair bit bigger, I think.

      FWIW, I’d personally drop the named type requirement there, use proxy, and just def the result of (class (create-proxy-instance …)) for instance? checks and such (assuming that that’s why you need the named type).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s