Suppose I define a record called Node: (defrecord Node [tag attributes children])
.
After this definition, according to the docstring of defrecord
a factory function called ->Node
is defined, as well as another factory function map->Node
and a Java class constructor Node.
.
I'm wondering what exactly the difference is between the positional factory function ->Node
and the constructor Node.
, apart from the normal differences between a Java class constructor / method on the one hand and a clojure function on the other (by normal differences I'm thinking things like the fact that functions are first-class in Clojure while methods are not).
(Update: see end of this answer for a note on primitive field types vs. parameter types of the ctor vs. parameter types of the factory.)
The positional factory just calls the constructor directly. The only interesting thing beyond that is that for records / types with large numbers of fields (namely over 20, which is the maximum number of positional arguments a Clojure function can accept) making the constructor call is slightly more involved (since you have to unpack some arguments from the rest-args seq); positional factories as emitted by defrecord
and deftype
handle that correctly, and moreover check that the correct number of arguments is supplied, throwing an appropriate exception if not.
This is documented in the docstring for the private function clojure.core/build-positional-factory
; say (doc clojure.core/build-positional-factory)
at the REPL to read it, or (source clojure.core/build-positional-factory)
to see the source.
The end result looks roughly like this:
;; positional factory for a type with up to 20 fields
(defn ->Foo
"Construct a Foo."
[x y z]
(new Foo x y z))
;; positional factory for a type with many fields
(defn ->Bar
"Construct a Bar."
[a b c d e f g h i j k l m n o p q r s t & overage]
(if (= (count overage) 2)
(new Bar a b c d e f g h i j k l m n o p q r s t
(nth overage 0) (nth overage 1))
(throw
(clojure.lang.ArityException.
(+ 20 (count overage)) (name '->Bar))))))
A note on parameter types:
Not sure if this falls under the rubric of "normal differences", so I'll mention it explicitly: deftype
/ defrecord
introduced classes may have fields of primitive types, in which case the corresponding parameters of the constructor will also be of primitive types. However, as of Clojure 1.5.1, the positional factories always take all-Object
arguments, even if technically they could be declared as primitive-accepting functions (that is, if the primitive types involved are long
and/or double
and there are at most four positional parameters).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With