This article is part of a series describing a port of the samples from Practical Common Lisp (PCL) to Clojure. You will probably want to read the intro first.
This article covers Chapter 17, Object Reorientation: Classes.
Common Lisp defines classed with
defclass. In Clojure, I can define structs with
bank-account struct has two basis keys:
balance. I can specify values for these keys, in the order they were declared, using
struct, all the basis keys are optional:
If you prefer named parameters, you can use
struct-map instead of
Very important: structs are still maps. I can specify additional keys that are not part of the basis:
The examples below assume an
get to access a structure value:
But that' way too much effort. Structures are functions of their keys:
If the struct keys are symbols, I can go the other way. Symbols are functions of structs:
Other than symbols, what else can be a structure key? Ah, sweet immutability. Since Clojure data structures are immutable, any of them can function as keys.
I can use
dissoc to get a new map with a key added or removed:
But I can't
example-account because you can never remove a basis key:
Since structs are also maps, default values are easy: just
merge them. The example below doesn't even use a struct. (Often duck typing is good enough.)
If I want to validate fields, I can just write a validation function. Here is a validation that simply requires non-false values:
Of course, if I wanted to create tons of different structs with similar validations, I could build some helpers. Macros + metadata would be one way to go.
Clojure's structs fill some of the same roles as Common Lisp's classes. The exmaples above show how to create and access structs, and how to add default values and validation.
That said, Clojure's structs are not classes. They do not offer inheritance, polymorphism, etc. In Clojure, those kinds of jobs are handled by the incredibly flexible
defmulti (see the previous article for details, especially the references at the end).