nth on maps

103 views
Skip to first unread message

Shawn Hoover

unread,
Aug 22, 2008, 3:59:28 PM8/22/08
to clo...@googlegroups.com
While thinking about drewr's map destructuring question on IRC, I found that nth doesn't work on maps. Other group emails state this fact but I can't find that it's by design. (doc nth) says it works on sequences. Maps do work with seq, first, rest, etc, so it seems like nth should accept maps.

Changing IPersistentMap to implement Sequential fixed nth. But then I decided to move the change up to IPersistentCollection, since those all support seq and that also makes nth work on sets. I'm not up to date on how all the collection and sequence interfaces fit together, so I'm sure this breaks the conceptual integrity of something. Perhaps nth should check for instances of ISeq or IPersistentCollection or just call seq.

At any rate here is a test on a few collections:

(in-ns 'user)
(assert (= [:first :val] (nth {:first :val} 0)))
(assert (= :first (nth #{:first} 0)))
(assert (= :first (nth [:first] 0)))
(assert (= :first (nth (list :first) 0)))

And the change:

diff --git a/src/jvm/clojure/lang/IPersistentCollection.java b/src/jvm/clojure/lang/IPersistentCollection.java
--- a/src/jvm/clojure/lang/IPersistentCollection.java
+++ b/src/jvm/clojure/lang/IPersistentCollection.java
@@ -11,7 +11,7 @@
  */


-public interface IPersistentCollection {
+public interface IPersistentCollection extends Sequential{

 int count();

Chouser

unread,
Aug 22, 2008, 5:33:31 PM8/22/08
to clo...@googlegroups.com
On Fri, Aug 22, 2008 at 3:59 PM, Shawn Hoover <shawn....@gmail.com> wrote:
> While thinking about drewr's map destructuring question on IRC, I found that
> nth doesn't work on maps. Other group emails state this fact but I can't
> find that it's by design. (doc nth) says it works on sequences. Maps do work
> with seq, first, rest, etc, so it seems like nth should accept maps.

I think the argument has been that although nth might work on any seq,
it would be very inefficient for some collections (lists, maps, etc.)
compared to vectors where nth operates in constant time.

Do you have a use case in mind? It seems odd to me to try to use nth
on a map, where the order isn't meaningful and may not be stable. I
think in drewr's case it turned out he was actually trying to
destructure a MapEntry, not a map. Destructuring (and nth) work fine
for map entries:

user=> (def entry (find {:a 1} :a))
#'user/entry
user=> (nth entry 0)
:a
user=> (let [[k v] entry] (str "Key is " k ", val is " v))
"Key is :a, val is 1"

--Chouser

Shawn Hoover

unread,
Aug 22, 2008, 6:22:29 PM8/22/08
to clo...@googlegroups.com
On Fri, Aug 22, 2008 at 2:33 PM, Chouser <cho...@gmail.com> wrote:

On Fri, Aug 22, 2008 at 3:59 PM, Shawn Hoover <shawn....@gmail.com> wrote:
> While thinking about drewr's map destructuring question on IRC, I found that
> nth doesn't work on maps. Other group emails state this fact but I can't
> find that it's by design. (doc nth) says it works on sequences. Maps do work
> with seq, first, rest, etc, so it seems like nth should accept maps.

I think the argument has been that although nth might work on any seq,
it would be very inefficient for some collections (lists, maps, etc.)
compared to vectors where nth operates in constant time.

Do you have a use case in mind?  It seems odd to me to try to use nth
on a map, where the order isn't meaningful and may not be stable.

No use case, but the web docs for let do say that vector binding-exprs work on anything that supports seq. So this should work: (let [[a & others] {:foo :bar}] a)

If maps support seq and therefore the entire seq library (including first and rest which are used to compute nth on instances of Sequential), why wouldn't maps support nth for logical consistency with the seq abstraction?

Shawn

Rich Hickey

unread,
Aug 25, 2008, 12:46:14 PM8/25/08
to Clojure


On Aug 22, 3:59 pm, "Shawn Hoover" <shawn.hoo...@gmail.com> wrote:
> While thinking about drewr's map destructuring question on IRC, I found that
> nth doesn't work on maps. Other group emails state this fact but I can't
> find that it's by design. (doc nth) says it works on sequences. Maps do work
> with seq, first, rest, etc, so it seems like nth should accept maps.
>

There is a difference between a sequential view of a collection (i.e.
what you get from seq) and the collection itself. Maps are not
sequential data structures. nth is only supported for sequential data
structures, and that is by design.

If in some situation it makes sense to treat a map as sequential (it
might make some sense with array maps or sorted maps), just use (seq
m), which will serve as an indicator of that special point of view.

Rich

Shawn Hoover

unread,
Aug 25, 2008, 8:43:02 PM8/25/08
to clo...@googlegroups.com
Intuitively I knew that maps weren't inherently sequential, but when I saw that first and rest worked on them and saw how nth was implemented, I kept thinking nth should join the "I'll call seq for you" free-for-all. I'll buy it since it's by design.

Based on this discussion, the docs, and what I gathered from the implementation, my current model is:
  1. Data structures such as vectors, lists, strings, and Arrays are inherently sequential. This property is denoted case-by-case in the docs and the implementation of nth and formally by the interface clojure.lang.Sequential. The latter happens to be implemented by IPersistentVector, IPersistentList, and ASeq.
  2. Sequences are logical sequential views of other data structures that are not inherently sequential, such as maps and hash-sets. This capability is obtained by implementing ISeq (first, rest, cons) and, by inheritance, IPersistentCollection (seq, count, a different cons).
  3. nth provides direct support for inherently sequential things and for sequences. However, nth is not part of the seq library; you must explicitly pass the seq of anything else. nth happens to cast instances of Sequential to IPersistentCollection in order to call seq and get the nth rest, but this is an implementation detail and does not mean that nth supports directly all instances of ISeq or IPersistentCollection.
  4. Vector binding destructuring works on things that nth supports.
  5. The bottom line is that nth accepts sequences but it will not call seq for you if you pass anything else.
Sorry if this is too focused on the implementation, but for some reason that was key for me to get out of my head that nth should call seq on everything.

Thanks for explaining,
Shawn
Reply all
Reply to author
Forward
0 new messages