Why does Clojure have 5 ways to define a class instead of just one?

12,879

Solution 1

This is a mix of three different factors:

  1. The particular type system of the jvm
  2. The need for slightly different semantics for different use cases when defining types
  3. The fact that some of these were developed earlier, and some later, as the language has evolved.

So first, let's consider what these do. deftype and gen-class are similar in that they both define a named class for ahead-of-time compilation. Gen-class came first, followed by deftype in clojure 1.2. Deftype is preferred, and has better performance characteristics, but is more restrictive. A deftype class can conform to an interface, but cannot inherit from another class.

Reify and proxy are both used to dynamically create an instance of an anonymous class at runtime. Proxy came first, reify came along with deftype and defrecord in clojure 1.2. Reify is preferred, just as deftype is, where the semantics are not too restrictive.

That leaves the question of why both deftype and defrecord, since they appeared at the same time, and have a similar role. For most purposes, we will want to use defrecord: it has all the various clojure goodness that we know and love, sequability and so forth. Deftype is intended for use as a low level building block for the implementation of other datastructures. It doesn't include the regular clojure interfaces, but it does have the option of mutable fields (though this isn't the default).

For further reading check out:

The clojure.org datatypes page

The google group thread where deftype and reify were introduced

Solution 2

The short answer is that they all have different and useful purposes. The complexity is due to the need to interoperate effectively with different features of the underlying JVM.

If you don't need any Java interop then 99% of the time you are best off sticking with either defrecord or a simple Clojure map.

  • Use defrecord if you want to use protocols
  • Otherwise a regular Clojure map is probably simplest and most understandable

If your needs are more complex, then the following flowchart is a great tool for explaining why you would choose one of these options over the others:

http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/

Flowchart for choosing the right clojure type definition form

Share:
12,879
Salil
Author by

Salil

Most of my experience is in Java, C/C++, Python, Ruby, and Common Lisp. Wannabe functional programming guru.

Updated on June 05, 2022

Comments

  • Salil
    Salil about 2 years

    Clojure has gen-class, reify, proxy and also deftype and defrecord to define new class-like datatypes. For a language that values syntactic simplicity and abhors unnecessary complexity, it seems like an aberration. Could someone explain why it is so? Could Common Lisp-style defclass have sufficed?

  • Salil
    Salil almost 13 years
    The google group thread was very valuable. My understanding is for java-interop proxy, gen-class are supplanted by reify and deftype mostly. I am glad that we have fewer 'recommended' ways to define types now.
  • Salil
    Salil almost 13 years
    thanks a lot for the link to flow-chart. It helps that the blog is by the co-author of O'reilly's - Programming Clojure. I think this decision-making is quite complex. Are the differences between interface and concrete class or need to define static methods or not or named type or anonymous type are so vast that they need a different language construct?
  • mikera
    mikera almost 13 years
    the differences are not vast, but they are philosophically different in terms of what you are trying to achieve. I think the multiple approaches in Clojure reflect these underlying differences, which is a good reason why they should be given different names.
  • Rob Lachlan
    Rob Lachlan about 9 years
    @Trylks : The ability to treat the object as a sequence of key-value pairs. Pretty much everything that's native to clojure can be treated as a sequence, which is very powerful.
  • Mars
    Mars about 9 years
    proxy is sometimes preferred because it allows modification of methods at runtime. (e.g. The Joy of Clojure, 2nd ed. suggests this might be handy for modifying callbacks in a web handler.)
  • Mars
    Mars about 9 years
    The flowchart is great, but doesn't cover all of the possibilities or reasons for using one or another options, or combinations of them. No simple chart could.