I just stumbled on a rather nice feature of defrecord
that I've missed in the past; perhaps you have as well?
I've been using Stuart Sierra's component library to manage startup of the server I'm building.
A big part of component is the use of records. You define a record that stores your own data and have the record extend component/Lifecycle
; this is provided to the component library and it will assoc
in any dependencies before invoking the start
method.
Now, in practice, the values of the record's fields, execution-mode
, active-connection
, etc., don't change (at least, the component library doesn't change them). So the easiest thing is to just reference those symbols in the body of the start
or stop
method.
But what about other cases, where the field values may change? Do you need to to access the values via keyword? That is, if the field values are changed and you use the symbols, do you get the current value, or the value as passed to the record's constructor?
That will, of course, work ... but it's actually not necessary. From the defrecord
documentation:
Note that method bodies are not closures, the local environment includes only the named fields, and those fields can be accessed directly.
Our answer is right there in this typically too-terse description. The method bodies look like closures over the initial values of the fields ... but they are not; they are translated into references to the actual fields.
Here's a simpler version of StateImpl
, with a simple experiment to prove its behavior:
I haven't looked at the bytecode generated, but it appears that referencing a field in a method body is very similar to accessing a field in a normal Java object; the value retrieved is based on the this
-like first parameter. In other words, it's designed to be very natural for Java developers. Even so, this behavior comes as a welcome surprise ... though I'm pretty sure any of the current books on Clojure have this information.
Line 4 should be: State
ReplyDeleteUsing fields inside defrecord/deftype does just directly read the field using the Java bytecode getfield.
ReplyDelete@colt_nz thanks for the proof-reading
ReplyDelete