Consistency checks for records

TL;DR; how can I enforce consistency when instantiating records?

Longer version:
In my application I have defined several records with defrecord, and instances of this record get created a a few (3 or 4) places in the code, always using the map->... constructor. For example map->Dfa and map->State. A Dfa represents a finite state machine and a State represents a state within such a machine. Each Dfa has a collection of states, and each State has transitions which reference other states within the same Dfa.

Part of my development and debug time has been making sure that all transforms and combination functions which produce new Dfa object only have valid transitions, ie. only transitions to states in the same Dfa .

This is not really a constraint on the transformation functions, but is a constraint on the Dfa record.

What is the best way to enforce the consistency of an object (record) so that it is impossible to construct an inconsistent record?

One way would be to write a factory function my-map->Dfa, where I assert consistency, and the make sure no function calls map->Dfa directly, but always goes through my-map->Dfa.

Is that the correct way, or is there some way to augment construction of records in Clojure?

First thing that comes to mind is to advice the constructor (there are some advice libraries for Clojure, e.g., richelieu) with whatever validation code you’d need…

I understand that records are intended to be very simple objects, and the intent is to avoid implementing a full fledged object system with constructors, and hierarchy, and inheritance, and dependency management. To solve the problem at hand, I simply wrote a wrapper around map->Dfa and edited all my code to call the wrapper rather than map->Dfa directly. There were only 4 call-sites, including test cases.

I just hate re-inventing the wheel, and would rather use the capability built into the language when such is available.

There is no problem with writing your own constructor functions, I think that is the recommended approach.

Here Stuart Sierra recommends just adding your own constructor functions: https://stuartsierra.com/2015/05/17/clojure-record-constructors

1 Like