Kurt Schelfthout
Kurt Schelfthout

Reputation: 8990

Clojure record constructors not first class?

Apparently, you can't call apply with a record constructor:

(defrecord Foo. [id field])

(apply Foo. my-list)

fails at read time because it is not expecting Foo. in that place.

The only obvious workaround I could think of was to add a factory function:

(make-foo [id field] (Foo. id field))

which can be apply'ed of course.

Am I missing anything? I'd expect this from C#/Java but just thought it was a bit disappointing in Clojure...

Upvotes: 9

Views: 2370

Answers (3)

nickik
nickik

Reputation: 5881

The problem is known and there is lots of talk about it on the Clojure mailing list. More support will probably be added in future Clojure versions.

For now you have to use your own functions or use https://github.com/david-mcneil/defrecord2 which supports some features like:

  • print in an eval'able form
  • provide clojure function as constructor
  • accept named parameters (maps) in constructor
  • participate in pre/post walk multi-method

Upvotes: 3

Alex Miller
Alex Miller

Reputation: 70239

Circling back on this post-1.3....

In Clojure 1.3, defrecord creates two generated constructor functions. Given:

(defrecord Person [first last]) 

this will create a positional constructor function ->Person:

(->Person "alex" "miller")

and a map constructor function map->Person:

(map->Person {:first "string"})

Because this is a map, all keys are optional and take on a nil value in the constructed object.

You should require/use these functions from the ns where you declare the record, but you do not need to import the record class as you would when using the Java class constructor.

More details:

Upvotes: 11

Alex Miller
Alex Miller

Reputation: 70239

Foo. is a Java class constructor so it has typical Java interop constraints with how you call it. Creating a constructor function is a common solution (it also means you don't have to import the Foo when in a different namespace).

Upvotes: 6

Related Questions