Dynamic record creation

68 views
Skip to first unread message

Quzanti

unread,
Feb 3, 2011, 8:53:00 PM2/3/11
to Clojure
Hello. I need to dynamically define records

Suppose I have a record

(defrecord Person [name age])

Then to dynamically construct an instance I do a much more complex
version of

(concat [(symbol "Person.")] ["Peter"] [18])

Where things like Peter and the class of the record are actually the
results of complicated functions but I have just used simple values
here for illustration

The problem is I now have a lazy seq, not a record

I can do an eval

(eval (concat [(symbol "Person.")] ["Peter"] [18]))

But eval is evil. So I think my approach is wrong. Any suggestions
much appreciated

Kevin Downey

unread,
Feb 3, 2011, 9:23:39 PM2/3/11
to clo...@googlegroups.com
18:16 hiredman
http://groups.google.com/group/clojure/browse_thread/thread/83ad2eed5a68f108?hl=en
18:17 hiredman it amazes me how convoluted people can make things
18:17 brehaut hiredman: at least he recognises it
18:17 dnolen mattmitchell: word of advice, just do the simplest
thing. OO brainwashes people into
over-engineering, wasting time generalizing. Prolly
cause OO code is painful to
refactor, FP code less so.
18:18 hiredman brehaut: he parrots "eval is evil" but then does this
whole crazy concat thing
which has nothing to do with anything
18:18 brehaut oh sure, hes neck deep in crazy
18:18 mattmitchell dnolen: excellent thanks for the advice
18:18 hiredman '(Person. "Peter" 18) vs. (concat [(symbol "Person.")]
["Peter"] [18])
18:20 hiredman I dunno why he just doesn't create a factory function
18:20 brehaut if he's a noob maybe he thinks he is supposed to mash a
bunch of lists together and
then do some metaprogramming ?
18:20 brehaut out of fear that anything else isnt lisp?
18:21 hiredman I don't know
18:21 hiredman it's just bleh
18:22 hiredman like, you want to build a record from names that
resolve to values, but you want to
delay the binding of the names to values till runtime
18:22 -!- bridgethillyer
[~brid...@adsl-162-133-208.rmo.bellsouth.net] has joined #clojure
18:22 hiredman anyway, I will stop raving
[#clojure]

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?

David Nolen

unread,
Feb 3, 2011, 9:25:32 PM2/3/11
to clo...@googlegroups.com
On Thu, Feb 3, 2011 at 8:53 PM, Quzanti <quz...@googlemail.com> wrote:
Hello. I need to dynamically define records

For dynamic code like this it I would recommend using plain old maps.

David 

Quzanti

unread,
Feb 3, 2011, 9:36:43 PM2/3/11
to Clojure


On Feb 4, 2:23 am, Kevin Downey <redc...@gmail.com> wrote:

> whole crazy concat thing
>                 which has nothing to do with anything

I probably should have clarified that the reason I need concat is that
various functions are returning subsets of the arguments as vectors,
but as stated to keep things simple in the example I just used values

Aaron Cohen

unread,
Feb 3, 2011, 9:44:11 PM2/3/11
to clo...@googlegroups.com

Were you aware that records support assoc (and as a side-effect,
merge)? You end up with a new record as with all clojure immutable
datastructures, but this is how you can build records up "a piece at a
time".

(defn complicated-function [params]
(let [p (Person. nil nil)
parammap (zipmap [:name :age] params)]
(merge p parammap)))

user=> (complicated-function ["Aaron" 31])
#:user.Person{:name "aaron", :age 31}

--Aaron

David Nolen

unread,
Feb 3, 2011, 9:46:59 PM2/3/11
to clo...@googlegroups.com
On Thu, Feb 3, 2011 at 9:36 PM, Quzanti <quz...@googlemail.com> wrote:
I probably should have clarified that the reason I need concat is that
various functions are returning subsets of the arguments as vectors,
but as stated to keep things simple in the example I just used values

I still recommend using maps. But one possible way:

(ns foo)

(defrecord Person [name age])

(defn create-person [name age]
  (Person. name age))

(defn create [[ctor & args]]
  (apply ctor args))

(create (concat [create-person] ["Peter"] [18])) ;; #foo.Person{:name "Peter", :age 18}

I see no reason for the ctor to be defined as a string as you've done with "Person.".

Kevin Downey

unread,
Feb 3, 2011, 9:47:51 PM2/3/11
to clo...@googlegroups.com
if you really want to keep things simple you should just say
'(Person. "..." 18)
without all the concat noise the solution becomes obvious. you have a
form (new Person X Y), you want to execute the code with different
values bound to X and Y at runtime, sounds like a function to me.

Quzanti

unread,
Feb 3, 2011, 9:49:09 PM2/3/11
to Clojure
Thanks - that might well be part of the solution

Person. is dynamically determined (i.e the result of a fn too)

So I guess I am asking is there a way to dynamically resolve a
classname?

I found this

http://dev.clojure.org/jira/browse/CLJ-370?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel

Does anyone know anything more about it, or where the sourcecode would
be?

On Feb 4, 2:44 am, Aaron Cohen <aa...@assonance.org> wrote:

Quzanti

unread,
Feb 3, 2011, 9:51:21 PM2/3/11
to Clojure


On Feb 4, 2:47 am, Kevin Downey <redc...@gmail.com> wrote:
> if you really want to keep things simple you should just say
> '(Person. "..." 18)
> without all the concat noise the solution becomes obvious. you have a
> form (new Person X Y), you want to execute the code with different
> values bound to X and Y at runtime, sounds like a function to me.
>

No because Person is the result of a fn (constucted from Strings) the
the values are returned from other fns in groups

Kevin Downey

unread,
Feb 3, 2011, 9:55:46 PM2/3/11
to clo...@googlegroups.com
I strongly recommend against writing or designing anything that
requires dynamically generating defrecords. if you want to dynamically
generate classes I suggest getting familiar with the asm library and
reading up on classloaders.

Quzanti

unread,
Feb 3, 2011, 10:00:03 PM2/3/11
to Clojure


On Feb 4, 2:55 am, Kevin Downey <redc...@gmail.com> wrote:
> I strongly recommend against writing or designing anything that
> requires dynamically generating defrecords. if you want to dynamically
> generate classes I suggest getting familiar with the asm library and
> reading up on classloaders.
>

Its just the instances I am trying to create dynamically

I need to

(1) Dynamically resolve a classname (the class of the predefined
record)

(2) Merge it with vectors containing the values

Quzanti

unread,
Feb 3, 2011, 10:04:07 PM2/3/11
to Clojure


> I see no reason for the ctor to be defined as a string as you've done with
> "Person.".

The reason is that I am reading in XML and mapping a tag name to the
record class.

Kevin Downey

unread,
Feb 3, 2011, 10:04:25 PM2/3/11
to clo...@googlegroups.com
then define a factory function when you define the record, and use
that, you can easily apply a function to arbitrary arguments without
using eval

Quzanti

unread,
Feb 3, 2011, 10:10:06 PM2/3/11
to Clojure


On Feb 4, 3:04 am, Kevin Downey <redc...@gmail.com> wrote:
> then define a factory function when you define the record, and use
> that, you can easily apply a function to arbitrary arguments without
> using eval
>

Thanks. There may be something in that. Would there be an easy way of
dynamically determining which factory fn to call (as there is a record
for each possible XML tag)

Aaron Cohen

unread,
Feb 3, 2011, 10:11:11 PM2/3/11
to clo...@googlegroups.com

It's possible to do this using reflection, but I don't recommend it.

I think you're probably better off making a map of your tag names to
factory functions, then just applying the factory function to your
parameters.

(def record-factories {"Person #(Person. %1 %2)})

user=>(apply (record-factories "Person") ["Aaron" 31])
#:user.Person{:name "Aaron", :age 31}

Aaron Cohen

unread,
Feb 3, 2011, 10:14:32 PM2/3/11
to clo...@googlegroups.com
On Thu, Feb 3, 2011 at 10:11 PM, Aaron Cohen <aa...@assonance.org> wrote:
> On Thu, Feb 3, 2011 at 10:04 PM, Quzanti <quz...@googlemail.com> wrote:
>>
>>
>>> I see no reason for the ctor to be defined as a string as you've done with
>>> "Person.".
>>
>> The reason is that I am reading in XML and mapping a tag name to the
>> record class.
>
> It's possible to do this using reflection, but I don't recommend it.
>
> I think you're probably better off making a map of your tag names to
> factory functions, then just applying the factory function to your
> parameters.
>
> (def record-factories {"Person #(Person. %1 %2)})

Perils of copy-paste to an email window... that's missing a quotation mark.

Alan

unread,
Feb 4, 2011, 1:21:36 AM2/4/11
to Clojure
(defrecord Person [name])

(defmulti read-tag (fn [name & args] (keyword "myns" name)))
(defmethod read-tag :myns/person
[tag name]
(Person. name))

(read-tag "person" "david")
=> #:myns.Person{:name "david"}

But I agree with everyone else, there's no need to use records unless
you're addicted to objects and classes. Just build a map {:tag-name
"person" :name "david"}, or {:tag-name "person" :attrs {:name
"david"} :contents [...some nested stuff...]}.

On Feb 3, 7:11 pm, Aaron Cohen <aa...@assonance.org> wrote:
Reply all
Reply to author
Forward
0 new messages