Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Wednesday, June 04, 2014

Subtle nicety on defrecord

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.


colt_nz said...

Line 4 should be: State

Alex Miller said...

Using fields inside defrecord/deftype does just directly read the field using the Java bytecode getfield.

Howard Lewis Ship said...

@colt_nz thanks for the proof-reading