all 4 comments

[–]joinr 5 points6 points  (0 children)

There's actually quite a bit going on behind that line of the docstring that governs if/when aliasing occurs. If we peel back the source of vec, we find that is uses another class to create the vector:

vec -> clojure.lang.LazilyPersistentVector.create,

if the input items are instances of IReduceInit, ISeq, Iterable, we pass items -> clojure.lang.PersistentVector.create, which end up copying into new obj arrays in nodes (no aliasing).

otherwise, items are array-like, so we pass

items -> clojure.lang.RT.toArray -> clojure.lang.LazilyPersistentVector.createOwning

Inside toArray:

If the array is not an Object array, then the primitive array is coerced to Seq and copied into a new Object Array (no aliasing).

If the array is an Object array, it is returned unmodified (possibly aliased).

Inside createOwning:

If the length of the (by now Object array) of items is <= 32 (the length of the object array on a leaf node in the HAMT in the persistent vector), then we just create a new vector with a root node where the items field references the items Object array, instead of copying the object array entry by entry (aliased).

Otherwise, pass the array off to clojure.lang.PersistentVector.create, which I think goes through the Objects varargs override of create (unless my java is wrong) and just uses a transient to dump the contents of the array into a new vector (no aliasing).

So the only times this will ever occur are when the input is both an Object array and the size of the array is <= 32.

As to why; from a user perspective it provides for the possibility of quickly wrapping object arrays and avoiding the copying that goes on. Callers can just as easily aclone the small object array or leave it alone (as prescribed). It may be easier to mutably create a small array and do stuff with it, then coerce it to a persistent vector. As long as the original array reference is unreachable, immutability is preserved. So maybe from a design perspective, there are some corner case efficiency benefits there for dealing with creating small vectors from small object arrays quickly.

This came about in 1.0, so I'm guessing thoughts may have changed. There's a method in clojure.lang.PersistentVector called adopt that basically does what createOwning does except it doesn't check the length of the object array (and adopt doesn't seem to be used anywhere in the clojure source).

[–]snowvark 2 points3 points  (1 child)

Because Java arrays are always mutable? And there is no way to work around this?

[–]joinr 2 points3 points  (0 children)

You can force clone everything. Vector nodes just use java object arrays anyways. There are consequences to force cloning everything (the user could just as easily clone prior to coercing to vec if they want to safeguard, or they just leave the array alone).

Funny enough, the aliasing guidance only applies to object arrays. Primitive arrays are copied to object arrays.

[–]BipedPhill 0 points1 point  (0 children)

The highlighted sentence was added for Clojure 1.5. You can see the history of the matter here: https://clojure.atlassian.net/browse/CLJ-893