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.