开发者

Recommended macros to add functionality to Clojure's defrecord constructor?

defrecord in clojure allows for defining simple data containers with custom fields.

e.g.

user=> (defrecord Book [author title ISBN])
user.Book

The minimal constructor that results takes only positional arguments with no additional functionality such as defaulting of fields, field validation etc.

user=> (Book. "J.R.R Tolkien" "The Lord of the Rings" 9780618517657)
#:user.Book{:author "J.R.R Tolkien", :title "The Lord of the Rings", :ISBN 9780618517657}

It is always possible to write functions wrapping the default constructor to get more complex construction semantics - using keyword arguments, supplying defaults and so on.

This seems like the ideal scenario for a macro to provide expanded semantics. What macros have people written and/or recomm开发者_运维知识库end for richer defrecord construction?


Examples of support for full and partial record constructor functions and support for eval-able print and pprint forms:

  • http://david-mcneil.com/post/765563763/enhanced-clojure-records
  • http://github.com/david-mcneil/defrecord2

David is a colleague of mine and we are using this defrecord2 extensively in our project. I think something like this should really be part of Clojure core (details might vary considerably of course).

The things we've found to be important are:

  • Ability to construct a record with named (possibly partial) parameters: (new-foo {:a 1})
  • Ability to construct a record by copying an existing record and making modifications: (new-foo old-foo {:a 10})
  • Field validation - if you pass a field outside the declared record fields, throw an error. Of course, this is actually legal and potentially useful, so there are ways to make it optional. Since it would be rare in our usage, it's far more likely to be an error.
  • Default values - these would be very useful but we haven't implemented it. Chas Emerick has written about adding support for default values here: http://cemerick.com/2010/08/02/defrecord-slot-defaults/
  • Print and pprint support - we find it very useful to have records print and pprint in a form that is eval-able back to the original record. For example, this allows you to run a test, swipe the actual output, verify it, and use it as the expected output. Or to swipe output from a debug trace and get a real eval-able form.


Here is one that defines a record with default values and invariants. It creates a ctor that can take keyword args to set the values of the fields.

(defconstrainedrecord Foo [a 1 b 2]
  [(every? number? [a b])])

(new-Foo)
;=> #user.Foo{:a 1, :b 2}

(new-Foo :a 42)
; #user.Foo{:a 42, :b 2}

And like I said... invariants:

(new-Foo :a "bad")
; AssertionError

But they only make sense in the context of Trammel.


Here is one approach: http://david-mcneil.com/post/765563763/enhanced-clojure-records

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜