Datatypes and Protocols update

80 views
Skip to first unread message

Rich Hickey

unread,
Apr 22, 2010, 12:53:39 PM4/22/10
to Clojure
I have been doing some work cleaning up the design and implementation
of datatypes and protocols in preparation for the 1.2 release. Some
notable changes for those who have been working with the earlier
versions:

deftype/reify now take an explicit 'this' argument in method sigs.
The :as option is gone.

There is a new datatype construct, defrecord. defrecord includes the
implementation of persistent map, and the IPersistentMap magic of
deftype is gone.

deftype and defrecord create named classes, even in non-AOT use. There
is no factory fn created, instead you can just call the constructor.

The types for defrecord, deftype and definterface are automatically
imported into the defining namespace.

Substantial performance improvements to extend, and to higher-order
use of protocol fns.

In addition, I have started documenting these on the clojure.org site,
and you should use this documentation instead of the wiki design docs,
which will not be maintained.

http://clojure.org/protocols
http://clojure.org/datatypes

Please note that these are just higher-level descriptions, and contain
links to the detailed function docs, which are a must-read prior to
use.


Feedback and errata welcome as always,

Rich

--
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

Mark Engelberg

unread,
Apr 22, 2010, 1:30:19 PM4/22/10
to clojure
I tried using deftype relatively recently, but realized it wouldn't
work for my needs because serialization via *print-dup* wasn't yet
implemented. I'd recommend including this with the 1.2 release (or is
there a new recommended way to serialize Clojure data?)

Konrad Hinsen

unread,
Apr 22, 2010, 3:15:36 PM4/22/10
to clo...@googlegroups.com
On 22.04.2010, at 18:53, Rich Hickey wrote:

> Feedback and errata welcome as always,

One feature in the deftype/defrecord split that I regret is that defrecord no longer allows the redefinition of equals and hashCode. Any attempt to override those results in an error message about duplicate method definitions.

I have several former deftypes that are a perfect fit for the new defrecord, except that they need a specific comparison function. This is usually for excluding some fields from equality testing, or for requiring identity rather than equality for some fields.

As it is, I must convert all these to the new deftype, losing the convient field access through keywords.

Konrad.

ataggart

unread,
Apr 22, 2010, 5:37:08 PM4/22/10
to Clojure
On protocols:
- doc string coming after the arg vecs seems odd. I'm used to putting
them after the "name" of whatever I'm working on.

On protocols doc:
- "You can implement a protocol on nil ... Object": could you
elaborate on how these work and/or provide examples? I think this will
solve the one problem I was running into earlier.
- "given a protocol my.ns/Protocol, an interface my.ns.MyProtocol":
the final segments don't match.




On Apr 22, 9:53 am, Rich Hickey <richhic...@gmail.com> wrote:
> I have been doing some work cleaning up the design and implementation
> of datatypes and protocols in preparation for the 1.2 release. Some
> notable changes for those who have been working with the earlier
> versions:
>
> deftype/reify now take an explicit 'this' argument in method sigs.
> The :as option is gone.
>
> There is a new datatype construct, defrecord. defrecord includes the
> implementation of persistent map, and the IPersistentMap magic of
> deftype is gone.
>
> deftype and defrecord create named classes, even in non-AOT use. There
> is no factory fn created, instead you can just call the constructor.
>
> The types for defrecord, deftype and definterface are automatically
> imported into the defining namespace.
>
> Substantial performance improvements to extend, and to higher-order
> use of protocol fns.
>
> In addition, I have started documenting these on the clojure.org site,
> and you should use this documentation instead of the wiki design docs,
> which will not be maintained.
>
> http://clojure.org/protocolshttp://clojure.org/datatypes

Jason Wolfe

unread,
Apr 22, 2010, 5:58:29 PM4/22/10
to Clojure
+1, I am also using this feature of the old deftype.

On Apr 22, 12:15 pm, Konrad Hinsen <konrad.hin...@fastmail.net> wrote:
> On 22.04.2010, at 18:53, Rich Hickey wrote:
>
> > Feedback and errata welcome as always,
>
> One feature in the deftype/defrecord split that I regret is that defrecord no longer allows the redefinition of equals and hashCode. Any attempt to override those results in an error message about duplicate method definitions.
>
> I have several former deftypes that are a perfect fit for the new defrecord, except that they need a specific comparison function. This is usually for excluding some fields from equality testing, or for requiring identity rather than equality for some fields.
>
> As it is, I must convert all these to the new deftype, losing the convient field access through keywords.
>
> Konrad.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.To post to this group, send email tocl...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email toclojure+...@googlegroups.com

Stuart Halloway

unread,
Apr 22, 2010, 5:54:08 PM4/22/10
to clo...@googlegroups.com
A good place to look for examples is protocols.clj and gvec.clj in
clojure itself. protocols.clj includes an example of implementing a
protocol on nil.

Stu

ataggart

unread,
Apr 22, 2010, 7:20:28 PM4/22/10
to Clojure
Ah, great! And of course the piece I as missing is that nil and
Object get supported via extend. Makes sense now given that that was
the section of the doc, but it didn't click the first time through.

MarkSwanson

unread,
Apr 22, 2010, 9:06:08 PM4/22/10
to Clojure
Minor errata barely worth mentioning:on the page: http://clojure.org/datatypes

employeee.getName()

employeee needs just 2 'e' characters.

Cheers.

Konrad Hinsen

unread,
Apr 23, 2010, 3:12:07 AM4/23/10
to clo...@googlegroups.com
On 22 Apr 2010, at 21:15, Konrad Hinsen wrote:

> I have several former deftypes that are a perfect fit for the new
> defrecord, except that they need a specific comparison function.
> This is usually for excluding some fields from equality testing, or
> for requiring identity rather than equality for some fields.

What I'd actually like to have for defrecord is an "equality" protocol
that I can implement myself or use a default implementation. That
protocol would have a function "equals" guaranteed to be called only
if the object compared to is of the same type. Object.equals would
take care of type testing and then call the protocol function. That
would remove the check-type-before-comparing-fields overhead that
every equals implementation for deftype and defrecord currently
requires.

ataggart

unread,
Apr 23, 2010, 11:49:42 AM4/23/10
to Clojure
One problem with requiring the "other" object to be of the same type
is that it would break the current model, e.g.:

user=> (= '(1 2 3) [1 2 3])
true

I'm left to wonder if it the more correct implementation of the
desired behavior is not to override the equals, but rather to
implement Comparable.

Rich Hickey

unread,
Apr 23, 2010, 1:20:48 PM4/23/10
to clo...@googlegroups.com

On Apr 22, 2010, at 3:15 PM, Konrad Hinsen wrote:

> On 22.04.2010, at 18:53, Rich Hickey wrote:
>
>> Feedback and errata welcome as always,
>
> One feature in the deftype/defrecord split that I regret is that
> defrecord no longer allows the redefinition of equals and hashCode.
> Any attempt to override those results in an error message about
> duplicate method definitions.
>
> I have several former deftypes that are a perfect fit for the new
> defrecord, except that they need a specific comparison function.
> This is usually for excluding some fields from equality testing, or
> for requiring identity rather than equality for some fields.

It is a design goal of defrecord that the types it creates have
uniform semantics. As soon as you have user-defined equality you can
get broken semantics.

>
> As it is, I must convert all these to the new deftype, losing the
> convient field access through keywords.
>

You can get that back easily by implementing ILookup/IKeywordLookup,
as does defrecord. I still have ideas about macro-like mixins for use
in deftype, but I concluded the mandatory use of such mixins to create
record-like things was too much user effort, and we need more
experience with code reuse in deftype to determine need.

Rich

Rich Hickey

unread,
Apr 23, 2010, 1:21:47 PM4/23/10
to clo...@googlegroups.com

On Apr 23, 2010, at 3:12 AM, Konrad Hinsen wrote:

> On 22 Apr 2010, at 21:15, Konrad Hinsen wrote:
>
>> I have several former deftypes that are a perfect fit for the new
>> defrecord, except that they need a specific comparison function.
>> This is usually for excluding some fields from equality testing, or
>> for requiring identity rather than equality for some fields.
>
> What I'd actually like to have for defrecord is an "equality"
> protocol that I can implement myself or use a default
> implementation. That protocol would have a function "equals"
> guaranteed to be called only if the object compared to is of the
> same type. Object.equals would take care of type testing and then
> call the protocol function. That would remove the check-type-before-
> comparing-fields overhead that every equals implementation for
> deftype and defrecord currently requires.
>

There's not much 'overhead' to that check.

Rich

Rich Hickey

unread,
Apr 23, 2010, 1:24:06 PM4/23/10
to clo...@googlegroups.com

On Apr 22, 2010, at 1:30 PM, Mark Engelberg wrote:

> I tried using deftype relatively recently, but realized it wouldn't
> work for my needs because serialization via *print-dup* wasn't yet
> implemented. I'd recommend including this with the 1.2 release (or is
> there a new recommended way to serialize Clojure data?)

print-dup is likely for defrecord, but not deftype (roll your own
there).

Rich

Krukow

unread,
Apr 23, 2010, 1:59:38 PM4/23/10
to Clojure
On Apr 22, 6:53 pm, Rich Hickey <richhic...@gmail.com> wrote:
[snip..]
> Feedback and errata welcome as always,
>

1) Typo on http://clojure.org/protocols:

Section "Basics"
defprotocol will automatically generate a corresponding interface,
with the same name as the protocol, i.e. given a protocol my.ns/
Protocol, an interface my.ns.MyProtocol.

my.ns.MyProtocol => my.ns.Protocol


2) Question. Could someone elaborate on the bullet

"You can implement a protocol on an interface"

Spefically the relation to multiple inheritance

3) When is 1.2 expected (roughly)?

Thanks,
/Karl

[snip..]

Konrad Hinsen

unread,
Apr 23, 2010, 2:14:13 PM4/23/10
to clo...@googlegroups.com
On 23.04.2010, at 17:49, ataggart wrote:

> One problem with requiring the "other" object to be of the same type
> is that it would break the current model, e.g.:
>
> user=> (= '(1 2 3) [1 2 3])
> true

I'd want this only for defrecord, which is a more limited (but also more convenient) way to define types. For the more basic and general deftype, everything should be possible.

However, a perhaps better way to arrive at the same goal is to provide a macro that takes care of the type comparison.

> I'm left to wonder if it the more correct implementation of the
> desired behavior is not to override the equals, but rather to
> implement Comparable.

Comparable requires a fully defined order relation, if I remember correctly. And it's not used by the standard Clojure comparison operators.

Konrad Hinsen

unread,
Apr 23, 2010, 2:24:14 PM4/23/10
to clo...@googlegroups.com
On 23.04.2010, at 19:20, Rich Hickey wrote:

> You can get that back easily by implementing ILookup/IKeywordLookup, as does defrecord. I still have ideas about macro-like mixins for use in deftype, but I concluded the mandatory use of such mixins to create record-like things was too much user effort, and we need more experience with code reuse in deftype to determine need.

That sounds very interesting. I have been playing with various ideas for reusing method code in several defttypes, but I am not really happy with any of them. Macros are difficult to use because nothing at the toplevel of a deftype form is macro-expanded. Syntax-quote is messy because it requires so many symbols to be prefixed with ~'. I ended up writing my own little templating system but its use is getting messier all the time as well.

Konrad.

Mark Engelberg

unread,
Apr 23, 2010, 11:48:46 PM4/23/10
to clojure
A few meandering observations:

I like the latest change to include a this argument. It makes the
number of arguments line up which is a good thing.

I like the idea of defrecord for the common case, rather than having
to request default implementations of various interfaces within
deftype. Still, I think the splitting of deftype and defrecord has
merely delayed, not eliminated, the need to eventually find a
convenient, general scheme for layering in default implementations of
interfaces. I look forward to seeing how this evolves.

I like Rich's description of Clojure's datatypes/protocols as
"opinionated, and mostly agree with his manifesto. The one opinion I
have the most difficulty with is:
"It has always been an unfortunate characteristic of using classes for
application domain information that it resulted in information being
hidden behind class-specific micro-languages, e.g. even the seemingly
harmless employee.getName() is a custom interface to data. Putting
information in such classes is a problem, much like having every book
being written in a different language would be a problem. You can no
longer take a generic approach to information processing. This results
in an explosion of needless specificity, and a dearth of reuse."

OO programmers write employee.getName() methods to preserve the option
of doing something more sophisticated later, which isn't readily
possible once all your clients start using employee.name everywhere.
So if you really want to create a generic approach to information
processing, it seems like the best approach is to address the
underlying reason that these custom interfaces are needed.

For this reason, I've always found appealing languages which let you
optionally write getter/setter methods that "hook into" the standard
field access syntax. This lets you start out with your fields public,
and let your clients use the standard field access "interface".
Later, if you realize you need to do something special, you can easily
add a custom getter without breaking your clients.

As far as I know, Clojure doesn't currently make any attempt to
address this problem of allowing a standard way to access public data
from an object, while preserving the option of doing something more
sophisticated later. So a programmer is still forced to choose
between the convenience of keyword lookup of data, versus a protocol
filled with "get-name" functions to preserve future flexibility.

Ideally, I'd like to see a way to allow me to write a program using
(:name employee), and later, if I need to, customize the employee
datatype so that (:name employee) actually dispatches to some other
function. Ditto with (assoc employee :name "Mark") (for example, to
validate the data in some way?).

If I'm off-base, and these issues are easy to currently workaround in
Clojure, I'd love to hear more about how others are handling this in
their own programs.

Michał Marczyk

unread,
Apr 24, 2010, 1:18:09 AM4/24/10
to clo...@googlegroups.com
On 24 April 2010 05:48, Mark Engelberg <mark.en...@gmail.com> wrote:
> Ideally, I'd like to see a way to allow me to write a program using
> (:name employee), and later, if I need to, customize the employee
> datatype so that (:name employee) actually dispatches to some other
> function.  Ditto with (assoc employee :name "Mark") (for example, to
> validate the data in some way?).

You can do that with deftype by implementing the appropriate
interfaces, like Rich mentioned in response to Konrad above. I've been
playing around with this actually:

http://gist.github.com/377480

Note that (:x (Foo. 1 2 3) nil) returns 1, while ((Foo. 1 2 3) :x)
returns 2. (It could be the other way around or whatever, anything
seems possible.)

Also note that for some reason (:x (Foo. 1 2 3) nil) works fine, while
(:x (Foo. 1 2 3)) throws an exception; not sure if I've run into a bug
here or simply done something silly (I'd expect the latter though).

Sincerely,
Michał

MarkSwanson

unread,
Apr 24, 2010, 9:51:18 AM4/24/10
to Clojure
>> You can no
>> longer take a generic approach to information processing. This results
>> in an explosion of needless specificity, and a dearth of reuse."

> For this reason, I've always found appealing languages which let you
> optionally write getter/setter methods that "hook into" the standard
> field access syntax.  This lets you start out with your fields public,
> and let your clients use the standard field access "interface".
> Later, if you realize you need to do something special, you can easily
> add a custom getter without breaking your clients.

I'd argue that leaky abstractions like getter/setter methods are evil,
and a good article (from a Java/imperative perspective) describing why
can be found here:
http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html?page=1

I think that the quote above from Rich is another good description of
why getter/setter methods are bad from a functional perspective.

Mike Meyer

unread,
Apr 24, 2010, 10:39:28 AM4/24/10
to clo...@googlegroups.com
On Sat, 24 Apr 2010 06:51:18 -0700 (PDT)
MarkSwanson <mark.sw...@gmail.com> wrote:

> >> You can no
> >> longer take a generic approach to information processing. This results
> >> in an explosion of needless specificity, and a dearth of reuse."
>
> > For this reason, I've always found appealing languages which let you
> > optionally write getter/setter methods that "hook into" the standard
> > field access syntax.  This lets you start out with your fields public,
> > and let your clients use the standard field access "interface".
> > Later, if you realize you need to do something special, you can easily
> > add a custom getter without breaking your clients.
>
> I'd argue that leaky abstractions like getter/setter methods are evil,
> and a good article (from a Java/imperative perspective) describing why
> can be found here:
> http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html?page=1

I think his fundamental assumption - that getter/setters reveal
details of the implementation - is wrong. They *can* do that, and
possibly in the Java world they normally do do that. But properly
used, attributes reveal information about the *state* of the object,
not about the implementation. Classic examples would be a queue object
with a "length" attribute, or a financial account object with a
"balance" attribute.

Neither of those attributes reveal information about the
implementation of the objects in question. They both reveal
information about the state that some client could find useful. They
are both values that, if not directly available from the object should
be calculated by the object, as calculating the value requires
knowledge about the implementation.

> I think that the quote above from Rich is another good description of
> why getter/setter methods are bad from a functional perspective.

Given that your attributes that aren't just random variables from the
implementation, but are reasonable things for a client to want to
know, then the unattributed inner quote nailed it: languages that
don't distinguish between reading the value of an attribute and
invoking a zero-argument method - or at least allow you to make the
latter look like the former - hide that specificity and encourage
reuse.

<mike
--
Mike Meyer <m...@mired.org> http://www.mired.org/consulting.html
Independent Network/Unix/Perforce consultant, email for more information.

O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Per Vognsen

unread,
Apr 24, 2010, 10:53:48 AM4/24/10
to clo...@googlegroups.com
A lot of these arguments go away with functional programming. With a
functional queue, you might as well store the length as a value
attribute because it won't ever change. In some cases I can see the
argument for on-demand computation of fields with referentially
transparent caching. That's where the delay form comes in. If you're
using the delay form to hide a very expensive computation, it's
probably good to express that in the interface by requiring explicit
forcing, rather than having an innocent-looking map lookup take
arbitrary time to execute because it forces the evaluation of a
delayed expression.

-Per

Richard Newman

unread,
Apr 24, 2010, 1:11:50 PM4/24/10
to clo...@googlegroups.com
> Neither of those attributes reveal information about the
> implementation of the objects in question. They both reveal
> information about the state that some client could find useful. They
> are both values that, if not directly available from the object should
> be calculated by the object, as calculating the value requires
> knowledge about the implementation.

This approach still isn't 'good' OO -- it might not leak
implementation details, but it supports asking, not telling.

http://pragprog.com/articles/tell-dont-ask

"That is, you should endeavor to tell objects what you want them to
do; do not ask them questions about their state, make a decision, and
then tell them what to do."

Rich Hickey

unread,
Apr 26, 2010, 8:15:48 AM4/26/10
to clo...@googlegroups.com
You can use functions if you don't like unencapsulated data.

Weren't you also advocating for serialization? Such things become much simpler when one knows they are dealing with just data, and not the results of some (possibly irreversible) function, of some other possibly uninitialized dependents etc.

You need a level that is the data (even under some 'property' system). The keyword-accessible, constructor-mapped fields are that layer. You can always build functions on top of that.

Calculated getters/setters are one of those rich sources of incidental complexity, with little real-world benefit IMO.

That said, defrecord is simply implemented in terms of deftype. People can easily prototype alternatives for consideration.

Rich

Rich Hickey

unread,
Apr 26, 2010, 8:29:56 AM4/26/10
to clo...@googlegroups.com

On Apr 24, 2010, at 1:11 PM, Richard Newman wrote:

>> Neither of those attributes reveal information about the
>> implementation of the objects in question. They both reveal
>> information about the state that some client could find useful. They
>> are both values that, if not directly available from the object
>> should
>> be calculated by the object, as calculating the value requires
>> knowledge about the implementation.
>
> This approach still isn't 'good' OO -- it might not leak
> implementation details, but it supports asking, not telling.
>
> http://pragprog.com/articles/tell-dont-ask
>
> "That is, you should endeavor to tell objects what you want them to
> do; do not ask them questions about their state, make a decision,
> and then tell them what to do."
>
>

To the extent OO tries to make information into objects, it isn't
'good', IMO. That article is a good example of how goofy things get
when you try to turn information into objects. Information is data.
Records are for information.

Why ever would I want to try to 'tell' a piece of information anything?

The datatype and protocol system is there to support the polymorphic
manipulation of data by functions, not for recreating OO rabbit holes.

Rich

Konrad Hinsen

unread,
Apr 26, 2010, 8:37:53 AM4/26/10
to clo...@googlegroups.com
On 24.04.2010, at 05:48, Mark Engelberg wrote:

> As far as I know, Clojure doesn't currently make any attempt to
> address this problem of allowing a standard way to access public data
> from an object, while preserving the option of doing something more
> sophisticated later. So a programmer is still forced to choose
> between the convenience of keyword lookup of data, versus a protocol
> filled with "get-name" functions to preserve future flexibility.

This is basically a question of how you choose your abstractions (the documented interfaces) and your implementations (the data structures), and in particular on where you draw the boundaries. That is a design issue, so I don't expect any language feature to remove the necessity to think about this carefully.

> For this reason, I've always found appealing languages which let you
> optionally write getter/setter methods that "hook into" the standard
> field access syntax.

That's little more than syntactic sugar, considering that field access is equivalent to calling a method with no arguments.

The closest equivalent in Clojure would be to define a functional accessor API for all access to your data. A first implementation would simply set the accessor to be a field-name keyword:

(defrecord foo [bar baz])
(def get-bar :bar)
(def get-baz :baz)

If necessary you can then replace the accessor functions by something more complicated, or turn them into prototype functions to allow multiple implementations. Client code won't see the difference.

Konrad.

Mark Engelberg

unread,
Apr 27, 2010, 3:20:51 AM4/27/10
to clojure
Watching Stuart's tutorial, it looks like the automatic factory
functions for deftypes have gone away (I'm still working with Clojure
1.1, so haven't had a chance to try the latest changes for myself).
I'm going to miss that feature, especially for defrecord, which is now
the "common case" construct.

I understand that you can always do "Foo." to construct a Foo record,
but these constructors don't act as full-fledged functions, right?

Honestly, for me the main issue is just that subjectively, it is less
satisfying to create a Clojure data structure and end up with
something that you construct with Java interop syntax. I'd like
Clojure data structures to look and feel "Clojurish" not "Javaish"
(yes, I know that Clojure is built with interop in mind, so
technically, anything Javaish is also Clojurish, but I still feel a
difference).

Konrad Hinsen

unread,
Apr 27, 2010, 3:33:00 AM4/27/10
to clo...@googlegroups.com
On 27 Apr 2010, at 09:20, Mark Engelberg wrote:

> I understand that you can always do "Foo." to construct a Foo record,
> but these constructors don't act as full-fledged functions, right?

No. They are not first-class objects (in fact, not objects at all in
the JVM sense), so you can't pass them around.

> Honestly, for me the main issue is just that subjectively, it is less
> satisfying to create a Clojure data structure and end up with
> something that you construct with Java interop syntax. I'd like
> Clojure data structures to look and feel "Clojurish" not "Javaish"

That was my initial reaction as well. However, I just write my own
factory functions now, and this gives me the opportunity to add
argument validation in any way I like.

BTW, another change is that the defined type is a Java class, whereas
before it was a var pointing to the factory function. Not being a var
means that the type doesn't really reside in the namespace. In
particular, a :use of the namespace doesn't get you the type, you have
to :import it.

Taken together, these changes make deftype and defrecord low-level
features for defining data types that are best exposed to the outside
world via a functional API. Your clients don't need to know that
there's a Java type with a constructor hidden somewhere.

Konrad.

Rich Hickey

unread,
Apr 27, 2010, 8:45:47 AM4/27/10
to clo...@googlegroups.com

On Apr 27, 2010, at 3:20 AM, Mark Engelberg wrote:

> Watching Stuart's tutorial, it looks like the automatic factory
> functions for deftypes have gone away (I'm still working with Clojure
> 1.1, so haven't had a chance to try the latest changes for myself).
> I'm going to miss that feature, especially for defrecord, which is now
> the "common case" construct.
>
> I understand that you can always do "Foo." to construct a Foo record,
> but these constructors don't act as full-fledged functions, right?
>
> Honestly, for me the main issue is just that subjectively, it is less
> satisfying to create a Clojure data structure and end up with
> something that you construct with Java interop syntax. I'd like
> Clojure data structures to look and feel "Clojurish" not "Javaish"
> (yes, I know that Clojure is built with interop in mind, so
> technically, anything Javaish is also Clojurish, but I still feel a
> difference).
>


I agree. I asked for suggestions for the factory fn in #clojure, but
got some pushback against introducing things in the namespace. I'm
still open to suggestions, here are the issues:

The generated type name is now imported, so at the very least the
factory fn can't have the same name as the class. Alternatives are
create-Foo etc.

Some have asked for parameterized factories:

(record Foo ...) or (record ::Foo ...)

These cannot be made as fast as direct factories. Also, they may be
used for key/value initialization:

(record ::Foo :field1 v1 :field2 v2 ...)

As soon as people want bodies for the factories, in order to do
argument transformation/validation/defaulting, a generated factory is
in the way.

We left it at: If you really want a factory you can always write one,
let's see if people do.

Rich

Konrad Hinsen

unread,
Apr 27, 2010, 10:18:54 AM4/27/10
to clo...@googlegroups.com
On 27.04.2010, at 14:45, Rich Hickey wrote:

> I agree. I asked for suggestions for the factory fn in #clojure, but got some pushback against introducing things in the namespace. I'm still open to suggestions, here are the issues:
>
> The generated type name is now imported, so at the very least the factory fn can't have the same name as the class. Alternatives are create-Foo etc.

How about providing the name of a factory function via an option, with no factory as the default? Something like

(defrecord Foo
[bar baz]
:factory create-Foo)

This would avoid the creation of vars that are not explicitly named in the code, and thus bad surprises.

Konrad.

Pedro Teixeira

unread,
May 22, 2010, 8:51:41 PM5/22/10
to Clojure

On Apr 27, 9:45 am, Rich Hickey <richhic...@gmail.com> wrote:
> On Apr 27, 2010, at 3:20 AM, Mark Engelberg wrote:
>
>
>
>
>
> > Watching Stuart's tutorial, it looks like the automaticfactory
> > functions for deftypes have gone away (I'm still working with Clojure
> > 1.1, so haven't had a chance to try the latest changes for myself).
> > I'm going to miss that feature, especially fordefrecord, which is now
> > the "common case" construct.
>
> > I understand that you can always do "Foo." to construct a Foo record,
> > but these constructors don't act as full-fledged functions, right?
>
> > Honestly, for me the main issue is just that subjectively, it is less
> > satisfying to create a Clojure data structure and end up with
> > something that you construct with Java interop syntax.  I'd like
> > Clojure data structures to look and feel "Clojurish" not "Javaish"
> > (yes, I know that Clojure is built with interop in mind, so
> > technically, anything Javaish is also Clojurish, but I still feel a
> > difference).
>
> I agree. I asked for suggestions for thefactoryfn in #clojure, but  
> got some pushback against introducing things in the namespace. I'm  
> still open to suggestions, here are the issues:
>
> The generated type name is now imported, so at the very least the  factoryfn can't have the same name as the class. Alternatives are  
> create-Foo etc.
>
> Some have asked for parameterized factories:
>
> (record Foo ...) or (record ::Foo ...)
>
> These cannot be made as fast as direct factories. Also, they may be  
> used for key/value initialization:
>
> (record ::Foo :field1 v1 :field2 v2 ...)
>
> As soon as people want bodies for the factories, in order to do  
> argument transformation/validation/defaulting, a generatedfactoryis  
> in the way.
>
> We left it at: If you really want afactoryyou can always write one,  
> let's see if people do.
>


Thought a dynamic factory is usefull. Here it goes my first macro, so
please let me know if I'm silly here.

(defmacro record
"Dynamic factory for defrecords."
([name] `(record ~name {}) )
([name vals-map]
(let [num-fields
(alength (.getParameterTypes (first (.getDeclaredConstructors
(class name)))))
args (repeat num-fields nil)]
`(merge (new ~name ~@args) ~vals-map))))


Usage:
(defrecord Bar [x y])
(record Bar)


cheers,
Pedro

Pedro Teixeira

unread,
May 23, 2010, 4:34:16 PM5/23/10
to Clojure
That previous attempt was not good. For any one interested, the
following macro might do the job:

(defmacro record
"Dynamic factory for defrecords."
([name] `(record ~name {}) )
([name vals-map]
`(let [con# (first (.getDeclaredConstructors ~name))
num# (alength (.getParameterTypes con#))]
(merge (.newInstance con# (make-array Object num#)) ~vals-map))))


So we can maintain a dynamic relation with defrecord:
(defrecord Bar [x y])
(record Bar)
(record Bar {:x 1))
Reply all
Reply to author
Forward
0 new messages