Why don't these two functions produce equivalent results?

53 views
Skip to first unread message

Bill Robertson

unread,
Jan 27, 2012, 3:41:18 PM1/27/12
to Clojure
I don't understand why the two functions below (recurs and transi) do
not produce the same result. To the best of my understanding doseq
will consume an entire sequence as demonstrated here:

user=> (doseq [x (range 0 10)] (print x))
0123456789nil

user=> *clojure-version*
{:major 1, :minor 3, :incremental 0, :qualifier nil}

user=> (defn recurs [dest src]
(if-let [element (first src)]
(recur (assoc dest element element) (rest src))
dest))
#'user/recurs
user=> (count (recurs {} (range 0 20000)))
20000

user=> (defn transi [dest src]
(let [temp (transient dest)]
(doseq [x src]
(assoc! temp x x))
(persistent! temp)))
#'user/transi
user=> (count (transi {} (range 0 20000)))
8
user=> (count (transi {} (range 0 20)))
8
user=> (count (transi {} (range 0 8)))
8
user=> (count (transi {} (range 0 7)))
7

Any ideas about why the first function does not behave the same as the
second?

Thanks

Kevin Downey

unread,
Jan 27, 2012, 3:50:28 PM1/27/12
to clo...@googlegroups.com

Please don't use transients unless you read and understand the documentation.

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

David Nolen

unread,
Jan 27, 2012, 4:04:10 PM1/27/12
to clo...@googlegroups.com
You can not bash transients like that. They must be operated on in a functional way.

David 

Bill Robertson

unread,
Jan 27, 2012, 4:08:19 PM1/27/12
to Clojure
I have read (doc transient), (doc assoc!) and (doc persistent!), and I
don't see what I'm missing, which is why I came here for help.

To my understanding, the first creates a transient collection, which
cannot be used in other threads, and cannot be used after converting
back to a persistent collection.

user=> (doc assoc!)
-------------------------
clojure.core/assoc!
([coll key val] [coll key val & kvs])
Alpha - subject to change.
When applied to a transient map, adds mapping of key(s) to
val(s). When applied to a transient vector, sets the val at index.
Note - index must be <= (count vector). Returns coll.

assoc!'s doc says that it adds mapping of keys to values when applied
to a map, which is what this code demonstrates.

user=> (def t (transient {}))
#'user/t
user=> (assoc! t 1 1)
#<TransientArrayMap clojure.lang.PersistentArrayMap
$TransientArrayMap@1352367>
user=> (assoc! t 2 2)
#<TransientArrayMap clojure.lang.PersistentArrayMap
$TransientArrayMap@1352367>
user=> (def p (persistent! t))
#'user/p
user=> p
{1 1, 2 2}

It says that it returns col. Does that imply that I'm supposed to hang
on to col?




On Jan 27, 3:50 pm, Kevin Downey <redc...@gmail.com> wrote:
> Please don't use transients unless you read and understand the
> documentation.
> On Jan 27, 2012 12:41 PM, "Bill Robertson" <billrobertso...@gmail.com>

David Nolen

unread,
Jan 27, 2012, 4:13:25 PM1/27/12
to clo...@googlegroups.com
On Fri, Jan 27, 2012 at 3:08 PM, Bill Robertson <billrob...@gmail.com> wrote:
I have read (doc transient), (doc assoc!) and (doc persistent!), and I
don't see what I'm missing, which is why I came here for help. 

The documentation here http://clojure.org/transients says:

"Don't bash in place"

Note that all the examples are done in a functional style - they actually use the value produced by each operation.

David

Bill Robertson

unread,
Jan 27, 2012, 4:17:39 PM1/27/12
to Clojure
I have read (doc transient), (doc assoc!) and (doc persistent!), and I
don't see what I'm missing, which is why I came here for help.

> Please don't use transients unless you read and understand the
> documentation.
> On Jan 27, 2012 12:41 PM, "Bill Robertson" <billrobertso...@gmail.com>

Bill Robertson

unread,
Jan 27, 2012, 4:24:26 PM1/27/12
to Clojure
Yes, I see that now. When I read "When applied to a transient map,
adds mapping of key(s) to val(s)" in the doc string, I understood that
to mean that it modified the existing map, and the (insufficient)
poking around that I did in the repl supported that. (actually did it
past 8 now - saw it fail in same way).

I guess under the hood its a essentially the same sort of persistent
data structure but with chunking (buffering) before it grows.

If I wanted to submit alternate wording for the doc string, how would
I do that? i.e. where is the process for contributing outlined?

e.g. "When applied to a transient map, adds mapping of key(s) to
val(s) in the resulting map."

Thanks
-Bill

On Jan 27, 4:13 pm, David Nolen <dnolen.li...@gmail.com> wrote:
> On Fri, Jan 27, 2012 at 3:08 PM, Bill Robertson
> <billrobertso...@gmail.com>wrote:

Andy Fingerhut

unread,
Jan 28, 2012, 8:36:08 PM1/28/12
to clo...@googlegroups.com
One way to think of it is that both assoc and assoc! create and return new maps that are different than the originals they were given as input.

assoc never modifies the original map.  assoc! might, or it might not.  It depends on implementation details.  A correct Clojure program will never rely on whether the original map is modified in place or not.  It should always use the return value of assoc!, and assume that the map given to assoc! now has a non-specified value.

I agree that the documentation could be clearer on this point.  I was going to point you at clojuredocs.org, but I see that the current example there is even more misleading, making it appear that you *should* "bash it in place".  I will probably fix that soon, but if someone beats me to it, all the better.

Andy


Reply all
Reply to author
Forward
0 new messages