Typedef-like functionality for Clojure records?

220 views
Skip to first unread message

Vincent Chen

unread,
Sep 26, 2013, 1:47:35 AM9/26/13
to clo...@googlegroups.com
Hi,

I have in my hands an XML description of data structures that I want
to mechanically translate into Clojure. There are <struct> and
<typedef> elements, with <struct> being similar to structs in C (list
of fields) and <typedef> being similar to typedef in C (defining new
name for already defined <struct>s).

I model <struct>s using Clojure records, but I'm not sure how to refer
to them with a new name when it comes to <typedef>s. Given the
following specification:

<struct name="foo">
<field name="a"/>
<field name="b"/>
</struct>

<typedef oldname="foo" newname="bar">

I would create records:

(defrecord foo [a b])
(defrecord bar [a b])

The problem is that struct foo and typedef bar lives in different
namespaces, i.e. I don't have foo's specification when I encounter
typedef bar. What I'd like to do is (def bar some-ns/foo), but what
about ->foo and map->foo?

So should I:
- (def bar some-ns/foo) (def ->bar some-ns/->foo) (def map->bar
some-ns/map->foo), while hoping that Clojure doesn't extend records
syntax/capabilities in the future?
- Use something else than records to model structs (suggestions welcome)?
- Other?

Thanks,

Vincent

david sheldrick

unread,
Sep 26, 2013, 4:31:33 AM9/26/13
to clo...@googlegroups.com
I'm not sure about your actual questions, but you should note that `foo` is not actually a var which you can reference like `some-ns/foo`. Rather, it is a Class. Consider:

user=> (defrecord Thing [a b])
user.Thing
user=> 

user=> (def OtherThing Thing)
#'user/OtherThing
user=> 

user=> (Thing. "hello" "sir")
#user.Thing{:a "hello", :b "sir"}
user=> 

user=> (OtherThing. "hello" "sir")
CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: OtherThing, compiling:(/private/var/folders/rz/2h_qlr5d2m7dts2xpshlz_nr000c_x/T/form-init123235568201547659.clj:1:1) 

So you might be wise to put another layer of abstraction in there if you need to alias the class in addition to it's constructor functions.

Marshall Bockrath-Vandegrift

unread,
Sep 26, 2013, 4:49:36 AM9/26/13
to clo...@googlegroups.com
Vincent Chen <nood...@gmail.com> writes:

> - Use something else than records to model structs (suggestions welcome)?

Maps.

Records have concrete Java types, which allows them to implement
interfaces and participate in protocols. Fields defined on a record
type are backed by JVM object fields, which can increase performance.
But there are no strictness benefits – a record may have any number of
additional keys associated to values:

(defrecord Foo [bar])
;;=> user.Foo
(map->Foo {:bar 1, :baz 2})
;;=> #user.Foo{:bar 1, :baz 2}
(class (map->Foo {:bar 1, :baz 2}))
;;=> user.Foo

So my suggestion would be to instead turn your `struct` definitions into
functions validating that the expected fields are present within plain
maps. (Assuming some sort of strictness/validation is the goal.)

-Marshall

Jason Wolfe

unread,
Sep 26, 2013, 8:21:41 PM9/26/13
to clo...@googlegroups.com
I'm not sure if this would meet your need (it doesn't help with your proximate defrecord issue), but if you're primarily interested in documentation and validation you could consider prismatic/schema [1] as a translation target, and just represent your data with maps.

Vincent Chen

unread,
Sep 28, 2013, 12:19:50 AM9/28/13
to clo...@googlegroups.com
Thanks for the suggestion. I'll take a look at prismatic/schema.
> --
> --
> 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
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+u...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Vincent Chen

unread,
Sep 28, 2013, 12:36:08 AM9/28/13
to clo...@googlegroups.com
Thanks for the suggestions.

My records (structs) do actually implement protocols, since the
structs represent something which has to be sent over the wire.
Clojure being a dynamic language means that I have to specify the
types of each field of a struct, and I do it as part of a protocol
implemented by the records.

If I'm getting you correctly, you're suggesting that instead of

(defrecord Foo [a b c])

I should rather have

(defn make-foo [a b c]
{:a a, :b b, :c c})

? That could work, even though I had tried to avoid having to do this
when I started. I guess rather than protocols I'll just use
multimethods instead.

Thanks for your help,

Vincent
Reply all
Reply to author
Forward
0 new messages