Any downside of record compared to map

955 views
Skip to first unread message

Warren Lynn

unread,
Jul 22, 2012, 7:42:33 PM7/22/12
to clo...@googlegroups.com
I plan to change all my major data structures to records instead of plain maps. Since record has everything a map provides, I figure there won't be any harm. But is that really so? Would appreciate the opinions from people who know better.

Lee Spector

unread,
Jul 22, 2012, 9:25:19 PM7/22/12
to clo...@googlegroups.com
On Jul 22, 2012, at 7:42 PM, Warren Lynn wrote:

> I plan to change all my major data structures to records instead of plain maps. Since record has everything a map provides, I figure there won't be any harm. But is that really so? Would appreciate the opinions from people who know better.

I know I'm in the minority but I happen to prefer maps to records, and in fact I really like struct-maps which I gather may (?) not be long for this world. Nonetheless, since you asked, following are two old messages in which I attempted to point out some of the things that I like about struct-maps as opposed to records.

------------------

On Jul 28, 2011, at 8:19 PM, Alex Osborne wrote:
> The main use cases for the old structs feature on the other hand are
> completely replaced by records and it is recommended that new code use
> defrecord instead of defstruct.

I had some code using structs and, following advice like this, replaced it with code using records.

The result was more complicated, uglier and not noticeably faster.

I reverted to the struct version.

I like structs, and I hope they are retained!

-Lee

PS for anyone who is curious the struct version of the code in question is as follows (with push-types having a value like '(:exec :integer :float :code :boolean :auxiliary :tag :zip)):

(defmacro define-push-state-structure []
`(defstruct push-state ~@push-types))

(define-push-state-structure)

(defn make-push-state
"Returns an empty push state."
[]
(struct-map push-state))

The record version that I came up with is:

(defn keyword->symbol [kwd]
"Returns the symbol obtained by removing the : from a keyword."
(read-string (name kwd)))

(defmacro define-push-state-record-type []
"Defines the pushstate record type. The odd trick with read-string was a hack to
avoid namespace qualification on the pushstate symbol."
`(defrecord ~(read-string "pushstate") [~@(map keyword->symbol push-types)]))

(define-push-state-record-type)

(defmacro make-push-state
"Returns an empty push state."
[]
`(pushstate. ~@(map (fn [_] nil) push-types)))

Maybe there's a better way -- I wouldn't doubt it -- but the struct version was simple and worked exactly like I wanted it to, so I decided to stop tinkering.
-----------------

On Aug 29, 2011, at 2:57 PM, Tassilo Horn wrote:
>
> I guess the problem with that is that you need to have an instance of
> the record before you can use `keys'. And to create an instance, at
> least you have to know the number of keys.
>
> However, you can inspect the record's constructor using reflection:

Or, if structs will do for your application, then you can do this more simply:

user=> (defstruct mystruct :a :b :c)
#'user/mystruct
user=> (keys (struct-map mystruct))
(:a :b :c)

-Lee

----------------------

On Jan 18, 2011, at 7:21 PM, Chas Emerick wrote:
> You might find my minor sugar for defining records whose slots have default values:
>
> http://cemerick.com/2010/08/02/defrecord-slot-defaults/

Nice -- I do think it'd be good for something like this to be built in.

> Also worth noting is the further enhancement by Michael Fogus (linked to in the comments on the above post) that adds support for kwargs to the factory function for the defined record.

Oh yes, keyword args are another nice thing about struct-map that I'd forgotten about but which have also been important to me in some contexts, e.g. when there are lots of args.

> I also like a few aspects of what structmaps provide vs. regular maps (in particular, documentation of intent), but records have too many far more significant advantages and are too easy to add sugar to to reclaim the minor bits structmaps offer to not use IMO.

If the above conveniences get baked in then I'll probably agree.

----------------------

Sean Corfield

unread,
Jul 22, 2012, 9:58:20 PM7/22/12
to clo...@googlegroups.com
On Sun, Jul 22, 2012 at 6:25 PM, Lee Spector <lspe...@hampshire.edu> wrote:
> I know I'm in the minority but I happen to prefer maps to records, and in fact I really like struct-maps which I gather may (?) not be long for this world. Nonetheless, since you asked, following are two old messages in which I attempted to point out some of the things that I like about struct-maps as opposed to records.

I don't think you're in the minority. I prefer regular maps to records
in general. struct-map was deprecated "a long time ago" (in Clojure
1.2). clojure.java.jdbc stopped using struct-map a while back - at the
recommendation of Clojure/core - in favor of regular maps.

Chas Emerick's flowchart is very helpful here I think:
http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Warren Lynn

unread,
Jul 22, 2012, 11:54:03 PM7/22/12
to clo...@googlegroups.com
I don't think you're in the minority. I prefer regular maps to records
in general. struct-map was deprecated "a long time ago" (in Clojure
1.2). clojure.java.jdbc stopped using struct-map a while back - at the
recommendation of Clojure/core - in favor of regular maps.

Chas Emerick's flowchart is very helpful here I think:
http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/


Could you elaborate a little bit more? I know there is a chart, but the chart does not tell you why. Again, if record provides everything a map can provide, why would you prefer regular map? I thought at most you would say "it does not make much difference", and that would mean "no harm" to me.

Sean Corfield

unread,
Jul 23, 2012, 1:05:09 AM7/23/12
to clo...@googlegroups.com
On Sun, Jul 22, 2012 at 8:54 PM, Warren Lynn <wrn....@gmail.com> wrote:
> Could you elaborate a little bit more? I know there is a chart, but the
> chart does not tell you why.

Hmm, I thought the flowchart gave pretty good reasons, sorry :(

Records aren't as flexible and you'll lose the type anyway when
general map operations are run on them. For example, (into {} (map
something my-record)) - and you can't construct an empty record, the
declared fields must be present, and your type bleeds into a lot of
aspects of your code so it's much less generic (with map->MyRecord
calls and MyRecord. constructions, for example).

That sort of thing hardly ever makes sense for the type of problems I'm solving.

Per the flowchart, if I needed to interop with Java and represent
domain objects in Java or I needed protocol dispatch on specific
types, I'd use defrecord or deftype. But I don't need those features
so I stick with generic Clojure data types - maps and vectors and
sets.

Hope that clarifies?

Takahiro Hozumi

unread,
Jul 23, 2012, 1:07:57 AM7/23/12
to clo...@googlegroups.com
I think defrecord has 5 downsides compared to regular map.

1. The literal of regular map is eye-friendly and portable.
However I still don't know how helpful instant literals added in clojure 1.4 is for records.

2. The construction of record depends on an order of arguments.
Sometimes, I feel that a lack of construction with key-value style is not convenient.

3. Replacing all regular maps to records simply make code volume increase.

4. Records easily loose their type.
(merge {:c 3 :d 4} (Foo. 1 2))
;=> {:a 1, :b 2, :c 3, :d 4}

5. Regular map is a persistent data structure, which has internally efficient tree data structure, but record is compiled into class with fields as something like POJO.
I suspect efficiency of record when repeated assoc/dissoc.

But I could be wrong.

Regards,
Takahiro.

Baishampayan Ghose

unread,
Jul 23, 2012, 1:11:45 AM7/23/12
to clo...@googlegroups.com
On Mon, Jul 23, 2012 at 10:37 AM, Takahiro Hozumi <fat...@googlemail.com> wrote:
> 2. The construction of record depends on an order of arguments.
> Sometimes, I feel that a lack of construction with key-value style is not
> convenient.

(defrecord Foo ...

will give you `->Foo` & `map->Foo` for free. The second one accepts a
map as input.

Regards,
BG

--
Baishampayan Ghose
b.ghose at gmail.com

Takahiro Hozumi

unread,
Jul 23, 2012, 1:19:09 AM7/23/12
to clo...@googlegroups.com
Baishampayan
I didn't know `map->Foo`. Thank you for the infomation!

Bronsa

unread,
Jul 23, 2012, 6:01:21 AM7/23/12
to clo...@googlegroups.com
there's also the reader literal
user=> (defrecord foo [bar baz])
user.foo
user=> #user.foo{:baz 1 :bar 2}
#user.foo{:bar 2, :baz 1}



2012/7/23 Takahiro Hozumi <fat...@googlemail.com>
--
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

Lee Spector

unread,
Jul 23, 2012, 8:20:40 AM7/23/12
to clo...@googlegroups.com

Considering that maps do have upsides compared to records in some cases (as indicated, e.g., by Chas's flowchart), and that struct-maps add a couple of handy features in the context of some uses of maps, can anybody say why struct-maps are deprecated?

-Lee

Ben Mabey

unread,
Jul 23, 2012, 2:59:23 PM7/23/12
to clo...@googlegroups.com
Another downside I have ran into that hasn't been mentioned is with
(Java) serialization. If you are using records and defining protocols
inline you can serialize the record just fine, however if you change the
implementation then all of your old serialized records are incompatible
even if the change was superficial. The Java solution to this problem
is to define a 'static final long serialVersionUID' however AFAIK there
is no way to define this on a Clojure record. (If someone knows better
please let me know since loosing data after every protocol change is
very annoying...) If you use multi-methods instead of protocols you
won't run into this issue.

-Ben

Aravindh Johendran

unread,
Jul 23, 2012, 4:06:39 PM7/23/12
to clo...@googlegroups.com

On Monday, July 23, 2012 8:20:40 AM UTC-4, Lee wrote:

Considering that maps do have upsides compared to records in some cases (as indicated, e.g., by Chas's flowchart), and that struct-maps add a couple of handy features in the context of some uses of maps, can anybody say why struct-maps are deprecated?

 -Lee
 
Are struct-maps really deprecated? I don't see a deprecation warning anywhere (clojure website, source, api, etc.). All I see is the following line in clojure website.
---> Note: Most uses of StructMaps would now be better served by records.
Is the use of struct-maps officially being discouraged? Is that the same as being "almost deprecated"? Will struct-maps go away in a future release?
 
 
 

Phil Hagelberg

unread,
Jul 23, 2012, 4:09:16 PM7/23/12
to clo...@googlegroups.com
On Mon, Jul 23, 2012 at 11:59 AM, Ben Mabey <b...@benmabey.com> wrote:
> Another downside I have ran into that hasn't been mentioned is with (Java)
> serialization. If you are using records and defining protocols inline you
> can serialize the record just fine, however if you change the implementation
> then all of your old serialized records are incompatible even if the change
> was superficial.

Another related downside is that during development, you'll reload
your defrecord forms, and each time you do so it creates a new class
that is visually indistinguishable from the last. Witness:

user> (defrecord x [a b c])
user> (def x1 (x. 1 2 3))
user> (defrecord x [a b c])
user> (def x2 (x. 1 2 3))
user> (= x1 x2)
false

-Phil

Lee Spector

unread,
Jul 23, 2012, 4:47:27 PM7/23/12
to clo...@googlegroups.com

On Jul 23, 2012, at 4:06 PM, Aravindh Johendran wrote:

> Are struct-maps really deprecated? I don't see a deprecation warning anywhere (clojure website, source, api, etc.). All I see is the following line in clojure website.
> ---> Note: Most uses of StructMaps would now be better served by records.
> Is the use of struct-maps officially being discouraged? Is that the same as being "almost deprecated"? Will struct-maps go away in a future release?
>

There's also this:

"Overall, records will be better than structmaps for all information-bearing purposes, and you should move such structmaps to defrecord. It is unlikely much code was trying to use structmaps for programming constructs, but if so, you will find deftype much more suitable."

from http://clojure.org/datatypes

FWIW there's also a comment on stackoverflow Michał Marczyk: "in fact, the latter is deprecated in 1.2" (http://stackoverflow.com/questions/4288360/clojures-macro-define-a-binding-whose-name-is-composed-from-an-argument).

I don't know if there's any more official statement anywhere. Myself, I really like struct-maps and I hope they stay around.

-Lee

Warren Lynn

unread,
Jul 23, 2012, 5:04:30 PM7/23/12
to clo...@googlegroups.com

Thank everybody for the discussion. I am now aware of some pitfalls in using defrecord.

For me, I like to have type information associated with my data. I still prefer defrecord over map in general, but I will proceed with more caution. I think some pitfalls here are not due to the greatness of regular map, but is because record is not yet so great (e.x. Python has no problem serializing typed data). But still, with current state of things, those downsides are real.

By the way, it seems record is not necessarily slower than map in map operations. Here is my test result:

(defrecord myrec [a b])
(def rec (myrec. 1 2))
(def m {:a 1 :b 2})


user=> (time (dotimes [x 10000000] (assoc m :a 4)))
"Elapsed time: 2499.155478 msecs"

user=> (time (dotimes [x 10000000] (assoc rec :a 4)))
"Elapsed time: 802.191248 msecs"

But I did not test other operations.

Sean Corfield

unread,
Jul 23, 2012, 11:55:25 PM7/23/12
to clo...@googlegroups.com
On Mon, Jul 23, 2012 at 1:47 PM, Lee Spector <lspe...@hampshire.edu> wrote:
> On Jul 23, 2012, at 4:06 PM, Aravindh Johendran wrote:
>> Are struct-maps really deprecated?

It's a good question.

Christophe Grand thinks they're "half deprecated" here:
http://www.adrianmouat.com/bit-bucket/2011/02/common-lisp-clojure-and-evolution/#comment-3932

Brian Carper expected them to be deprecated back in mid-2010 here:
http://stackoverflow.com/questions/2994618/in-clojure-how-do-i-access-keys-in-a-vector-of-structs#comment3062840_2994618

Sam Aaron thought they were deprecated in Clojure 1.2 here:
http://www.pawfal.org/dave/blog/2011/01/clojure-frustrations/comment-page-1/#comment-250

Andrew Brehaut thought they were due to be deprecated in 1.2 here:
http://brehaut.net/blog/2010/a_couple_of_lists

Alex Miller considers them deprecated here:
http://stackoverflow.com/questions/4575170/where-should-i-use-defrecord-in-clojure

Stuart Sierra says they "should probably be considered deprecated"
here: http://comments.gmane.org/gmane.comp.java.clojure.user/52628

Clojure Programming (Grand, Carper, Emerick) says "Clojure retains a
vestigial “struct” map implementation—via the def struct,
create-struct, struct-map, and struct functions—that should be
considered deprecated at this point, and avoided." on page 272.

Another reference to them being deprecated back in Clojure 1.2:
http://faustus.webatu.com/clj-quick-ref.html

But I agree it's hard to find an official statement of deprecation anywhere.
Reply all
Reply to author
Forward
0 new messages