deftype vs defrecord
Summary: There are two commonly used ways to create new data types in
Clojure, deftype
and defrecord
. They are similar but are intended to
be used in two distinct use cases. deftype
is for programming
constructs and defrecord
is for domain constructs.
In a recent lesson in PurelyFunctional.tv Online
Mentoring, I briefly mentioned
Clojure's defrecord
and deftype
, then quickly moved on. I think it's
worth exploring what they're used for and what their differences are.
To explain what the difference is, let's look at the field of accounting. Accounting is everywhere. Businesses of all types have accountants. An accountant at a hardware store definitely has to know accounting and also know a bit about hardware, which is the business domain. An accountant at an ice cream shop will probably learn about making and selling ice cream. But there is still a very clear division between accounting and the business domain. There are accounting terms and concepts that are clearly generic accounting terms. And then there are some ice cream-related terms that the accountant will use in the accounting. It's accounting about ice cream.
Clojure tries to help the programmer with the same conceptual division. When we're programming about ice cream, we're going to use a lot of computer/programming terms ("vector", "function", "string", etc.). But there will also be some new terms in our language that are representations of the domain, like "flavor", "recipe", "price", etc. Everything is divided into programming constructs and domain constructs.
If you're making a new programming construct, like a new data
structure, you want to make a new class but retain low level
control. You want to define the fields, say what protocols it
implements, and how equality is defined. That's what deftype
does.
deftype
defines a new type with specified fields (you can even make
them mutable) and one constructor and nothing else. Very barebones. Of
course you can implement protocols right inside the deftype
to define
functionality.
If you're making a new domain construct, you don't want low level.
That was a mistake Java made, forcing programmers to write tons of
domain classes and deck them out with getters. Each class was a new
thing, incompatible with any existing tools. In Clojure, you use
defrecord
if you want to create a domain type. You can think of
records like hashmaps but with their own class.
Like hashmaps, they have equality
and hash semantics defined for
you, as you would expect. And they can store arbitrary data using the
same access patterns as hashmaps.
You can assoc
, get
, count
, etc, on any record. Records will have
their own class and can implement protocols and interfaces. So you get
the best of using reusable data structures and type-based polymorphism.
If you don't need the polymorphism, you should probably just use a
hashmap.
This division between programming constructs and domain constructs is so helpful. It's an example of one beautiful thing about Clojure: it's a language helping you write software in a better way. You have different needs when you're making domain values and Clojure provides for those needs.
So, to wrap up, deftype
is for programming constructs and
defrecord
is for domain constructs that need a custom type. When
you're engineering your software, it's useful to ask if you're building
a programming construct or a domain construct and choose the right tool
for that job.
If you're interested in learning about how to use deftype
and
defrecord
, I have several examples in the PurelyFunctional.tv Online
Mentoring program. It's a step-by-step
program taking you from dabbler to Clojure professional. I release new
lessons all the time, in video form. You can get access by signing up
here.