A trivial question about type and class

29 views
Skip to first unread message

Chanwoo Yoo

unread,
Nov 6, 2008, 1:25:44 AM11/6/08
to Clojure
Hi all. In Stuart's book - Programming Clojure, there is a multi
method like following:

(defmulti blank? class)
(defmethod blank? String [s] (every? #{\space} s))
(defmethod blank? nil [_] true)

After reading the method, I was curious about type or class of native
data structures of Clojure. So I typed next code in slime. Hmm.. I
didn't understand why classes of {:a 1} and {} are not same. Like
Stuart's code, to make multi method branching based on clojure data
structure type(map, vector, list), what symbol should I use? (Like
String for "string", Is there a Map for {:a 1}? I tried
PersistentHashMap, HashMap, Map, so on.. I didn't find it.)

user> (= (class "abc") String)
true
user> (class {:a 1 :b 2})
#=clojure.lang.PersistentHashMap
user> (class {})
#=clojure.lang.PersistentHashMap
user> (= (class {:a 1}) (class {}))
false
user> (= (class {:a 1}) (class {:b 2}))
true

mac

unread,
Nov 6, 2008, 2:22:40 AM11/6/08
to Clojure
I don't have the book yet so I don't know what Stuarts intention was
with the example but here are my thoughts on the matter.
Dispatching based on type (class) makes more sense for java objects
such as a String than the Clojure data structures since Clojure
focuses more on value-equals semantics (which in dispatch would be
sort of like duck typing) than identity comparison.
For a discussion about dispatching based on type see this old thread:
http://groups.google.com/group/clojure/browse_thread/thread/74d430f8e4353725/d16b548d4d46aab0

/mac

Michael Wood

unread,
Nov 6, 2008, 2:27:22 AM11/6/08
to clo...@googlegroups.com
On Thu, Nov 6, 2008 at 8:25 AM, Chanwoo Yoo <chanw...@gmail.com> wrote:
>
> Hi all. In Stuart's book - Programming Clojure, there is a multi
> method like following:
>
> (defmulti blank? class)
> (defmethod blank? String [s] (every? #{\space} s))
> (defmethod blank? nil [_] true)
>
> After reading the method, I was curious about type or class of native
> data structures of Clojure. So I typed next code in slime. Hmm.. I
> didn't understand why classes of {:a 1} and {} are not same. Like
> Stuart's code, to make multi method branching based on clojure data
> structure type(map, vector, list), what symbol should I use? (Like
> String for "string", Is there a Map for {:a 1}? I tried
> PersistentHashMap, HashMap, Map, so on.. I didn't find it.)

I think what you're looking for is "clojure.lang.PersistentHashMap"
instead of just "PersistentHashMap".

user=> String
java.lang.String
user=> PersistentHashMap
java.lang.Exception: Unable to resolve symbol: PersistentHashMap in this context
[...]
user=> clojure.lang.PersistentHashMap
clojure.lang.PersistentHashMap
user=>

I don't know much about Clojure, though, so I suspect there is better
advice than the above :)

> user> (= (class "abc") String)
> true
> user> (class {:a 1 :b 2})
> #=clojure.lang.PersistentHashMap
> user> (class {})
> #=clojure.lang.PersistentHashMap
> user> (= (class {:a 1}) (class {}))
> false
> user> (= (class {:a 1}) (class {:b 2}))
> true

That seems rather strange to me too.

--
Michael Wood <esio...@gmail.com>

Chanwoo Yoo

unread,
Nov 6, 2008, 7:22:31 AM11/6/08
to Clojure
Thanks~ "clojure.lang.PersistentHashMap" works~ :) But I feel
something is strange.. See next code, especially arrowed line.

user> clojure.lang.PersistentHashMap
#=clojure.lang.PersistentHashMap
user> (= (class {:a 1 :b 2}) clojure.lang.PersistentHashMap)
true
user> (= (class {}) clojure.lang.PersistentHashMap)
true
user> (= (class {:a 1 :b 2}) (class {}))
true
user> (= (class {:a 1}) (class {})) ;; <=
false
user> (= (class {:a 1}) clojure.lang.PersistentHashMap) ;; <=
false
user> (= (class (hash-map :a 1)) clojure.lang.PersistentHashMap)
true
user> (= (class (hash-map :a 1)) (class {:a 1})) ;; <=
false
user> (:a {:a 1})
1

Is {:a 1} not a hash-map? It seems that there is some inconsistency...

On 11월6일, 오후4시27분, "Michael Wood" <esiot...@gmail.com> wrote:
> Michael Wood <esiot...@gmail.com>

Chanwoo Yoo

unread,
Nov 6, 2008, 7:30:17 AM11/6/08
to Clojure
Aha, I'm sorry for my laziness and spamming. {:a 1} is not a hash-map.
I guess there is some reason of performance about this... Anyway, as
mac pointed out, dispatching based on types will be inappropriate to
clojure data structures. Thank you mac for the thread link~

user> (class {:a 1})
#=clojure.lang.PersistentArrayMap

hoeck

unread,
Nov 6, 2008, 7:45:28 AM11/6/08
to Clojure


On Nov 6, 8:27 am, "Michael Wood" <esiot...@gmail.com> wrote:
> Michael Wood <esiot...@gmail.com>



On Nov 6, 8:27 am, "Michael Wood" <esiot...@gmail.com> wrote:
If you are just playing or if the class is only used once in your file/
namespace then this is absolutely okay (clojure does this in
boot.clj) :).
Or you type (import '(clojure.lang PersistentHasMap)),
or (ns my-namespace (:import (clojure.lang PersistentHashMap))) on top
of your file.

> > user> (= (class "abc") String)
> > true
> > user> (class {:a 1 :b 2})
> > #=clojure.lang.PersistentHashMap
> > user> (class {})
> > #=clojure.lang.PersistentHashMap
> > user> (= (class {:a 1}) (class {}))
> > false
> > user> (= (class {:a 1}) (class {:b 2}))
> > true
>
> That seems rather strange to me too.

This is a reader optimization. For very short hashmaps (currently 1
element), the clojure reader uses a clojure.lang.PersistentArrayMap
instead of a full fledged HashMap. The reader does this because he
knows that this map ({:a 1}) will only be constructed with one
MapEntry.

user> (= (class (into {} '([:a 1]))) (class {}))
true
Here the reader doesn't see any literal maps and (into {} '([:a 1]))
evals to a PersistentHashMap with one MapEntry.

Multimethods use the isa? function for dispatch, so to dispatch on a
Map type in general, one would use a java.util.Map or a
clojure.lang.IPersistentMap as the dispatch-value.

user> (and (isa? (class {}) java.util.Map) (isa? (class {:a 1})
java.util.Map))
true

There is also a similar optimization for clojure vectors. They are
constructed as c.l.LazilyPersistentVector which is basically a wrapper
arround a java array. Once you conj onto them, they turn into a
c.l.PersistentVector.

user> (let [v [1 2]] [(class v) (class (conj v 3))])
[#=clojure.lang.LazilyPersistentVector
#=clojure.lang.PersistentVector]

mb

unread,
Nov 6, 2008, 7:50:13 AM11/6/08
to Clojure
Hi,

On 6 Nov., 13:30, Chanwoo Yoo <chanwoo....@gmail.com> wrote:
> > Is {:a 1} not a hash-map? It seems that there is some inconsistency...

Clojure holds promises about the interface and the performance
characteristics of the provided functions. In general Clojure is
a lot about abstract interfaces. The underlying thing may change
as necessary to provide the best implementation for the specific
case.

An array map may be good for maps with a very small number of
entries, but inappropriate for maps with a big number of entries.
Since Clojure only promises the interface, it's free to decide
that assoc should now return a converted map for performance
reasons.

At least this is the impression I got from Rich's talk at - I think -
the Boston Lisp Group. I think he mentioned something like this.
Please correct me if I'm wrong.

Sincerely
Meikel

Rich Hickey

unread,
Nov 6, 2008, 8:17:44 AM11/6/08
to Clojure
You are right - this is the correct answer to the question.

Clojure is about programming to abstractions, among other things. So
you don't want to rely on the exact concrete types of things, but
rather the interfaces they implement.

There are many useful interfaces in Clojure. Chouser put together a
nice visualization:

http://clojure.googlegroups.com/web/chart.png

If you use class as a dispatch function in a multimethod, you can use
an interface, like IPersistentMap, as a dispatch value and it will
match all classes that extend IPersistentMap, since multimethod
dispatch uses isa?

There are also predicates that correspond to most of the interfaces,
like map?, list?, vector?, sorted? etc, all of which internally use
(instance? someInterface x). The aforementioned chart includes the
corresponding predicates.

In general, it is bad style to use class equality, as in (= (class
this) (class that)) - better to use instance? or isa?.

Rich

Chanwoo Yoo

unread,
Nov 6, 2008, 8:28:34 AM11/6/08
to Clojure
Thanks hoeck and mb for your detailed explanation! Now I can
understand your points. :) It's so exciting to read 'Programming
Clojure', and I'm really happy that 'a lisp book' is printed from 'The
Pragmatic Programmers', which is my favorite book publisher.

Chanwoo Yoo

unread,
Nov 6, 2008, 8:44:31 AM11/6/08
to Clojure
I really appreciate your answer... and exciting 'Clojure', Rich. :)
Reply all
Reply to author
Forward
0 new messages