Do you use records? Why?

I had some students recently inquire about records in Clojure, which got me thinking. From my perspective they are mainly holdovers from primitive Clojure (meaning both “the early days” and also “under the hood”). I’ve reckoned them as hold-overs from Java and holding appeal for Java veterans, which I’m not. In modern code I’ve heard of just two justifications:

  1. Some efficiency/optimization matters over raw maps
  2. Useful for sharing a prescribed map structure when your codebase will be used by many.

Is there more to it than this? If you’ve made good use of records, why?

3 Likes

Ya pretty much. They create a native type, which can then be used to efficiently delegate a protocol to its appropriate implementation. They also allow for faster access to their defined fields. The cool thing is, they are Maps as well, so you can start with a Map mostly, and move to a Record if you need type polymorphism to be faster or field access to be faster.

For type polymorphism, they have the advantage that the type is inherent to them. So say you wanted to switch based on the type of a Map, with a normal Map, you might need to manually add a :type key on the map and switch on that, maybe with a multi-method, but with a Record, the JVM will track the type, almost like metadata.

They can also help when you really need very strict sets of keys though Spec has kind of supplemented that use case, as you can just Spec a Map and provide a normal function to help you construct a valid one.

9 Likes

I recently stumbled upon and old IRC log of Rich Hickey discussing the naming of what ultimately became defrecord:
http://clojure-log.n01se.net/date/2010-03-25.html#08:59

rhickey: name game again…

so, using mixins for getting a full implementation of map is tricky and complicated, I think now the simplest thing will be for there to be a deftype based defstruct replacement that does that

And if you want to go further down a rabbit hole, here’s some talk a month later about clojure and OO, which seems to be on people’s minds due to the new addition of defrecord:

http://clojure-log.n01se.net/date/2010-04-21.html#08:09

3 Likes

You will probably use records if you want the fast, open polymorphism provided by protocols. “Why records” is thus addressed, in part, on https://clojure.org/reference/protocols.

Clojure has been impelled by a curiosity about how far up the performance curve the open, functional model* can go. There is pride in Clojure programs running “on the metal” (as it were) without layers of make-believe and inefficiency.

Records are a part of that quest, not only for brisk member access, but more notably for dynamic, type-based dispatch. Earlier, the Clojure programmer might use either Java interfaces (closed but fast) or multimethods (open but slow). Clojure protocols provide open, fast polymorphism. A record carries the types on which to dispatch.

  • The open/closed nomenclature comes from a Youtube. By Stuart Halloway or Rich Hickey, I think. Do you remember which speech it was?
5 Likes

Well-spoken! I like the perspective on functional programming “on the metal.” Being primarily a web app developer, that had never occurred to me (we like to pretend the metal is shrouded in clouds and doesn’t really exist). I’ll look more into protocols.What about with Clojurescript? Are records meaningful there?

Kind of implicit in things already said, but if you’re doing Java interop, Java code can do things with records; defrecord is one way to define a Java class. Not suitable for all interop uses, but very convenient when it is.

in general, if I have a data structure with fixed fields and a clear meaning, I feel it’s easier to understand code with a record rather than a map, because your code tells you that these data structures have a meaning–even if you don’t use protocols.