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.

3 comments:

  1. Line 4 should be: State

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

    ReplyDelete
  3. @colt_nz thanks for the proof-reading

    ReplyDelete

Please note that this is not a support forum for Tapestry. Requests for help will be deleted. Please subscribe to the Tapestry user mailing list if you are in need of support, or contact me directly for professional (for pay) support.

Spammers: Don't bother. I delete your comments and it's a waste of time for both of us. 垃圾邮件发送者:不要打扰。我删除您的评论和它的时间对我们双方的浪费