=> ({ :foo 1 :bar 2 } :foo)
1
the common pattern of defaulting to a value if the key isn't found is easy too: => ({ :foo 1 :bar 2 } :baz 4)
4
The above is a hash map, #{ ... } creates a hash set which otherwise has similar semantics. Sets, being function-callable, can be used directly for checking if something is one of a bunch of values: (if (#{301 302 303 307} response-code)
; we've been redirected
Maps and sets (you also have sorted maps and sets and a compact struct-map, all of which have identical semantics as the hash variants where appropriate) can additionally be used with most functions accepting a seq (list) or can be converted to a list explicitly using the seq function.Basically, maps and sets really are first-class citizens in clojure. Some of the flexibility is down to the fact that Clojure is a Lisp-1: these features are therefore hard to replicate in CL. (and yes, hash tables also bothered me somewhat in CL, altough other things probably bothered me more)
({ :foo 1 :bar 2 } :foo) ;; => 1
You get the equivalent by doing (:foo { :foo 1 :bar 2 }) ;; => 1
For default values: (:baz { :foo 1 :bar 2 } 3) ;; => 3
I vaguely remember this is because keys act as functions, and hashes act as functions as well. Out of habit and similarity to other languages, I never use the key-first notation, nor can I see where it is better to use it. (map :foo hashofhashes)
which would get the element :foo of every hash in hashofhashes (map #(% :foo) hashofhashes)
or if % has a chance of being nil: (map #(get % :foo) hashofhashes)I do sometimes use this notation, mostly when:
- the map could be nil. (get nil :foo) and (:foo nil) will return nil, but (nil :foo) will throw an exception.
- dealing with struct-maps, where the key can effectively be seen as an accessor function. (cf structs in CL)
- (rarely) when the expression evaluating to the map is long and complex. A keyword in the function position (a) makes it clear that what follows must be a map or set and (b) you don't have to start counting parens.
Does anyone do this sort of hash-table-as-function thing in Scheme?
While Clojure is certainly a good language, I think people are "switching" to it from CL due to hype, rather than their deep understanding of the problems with CL. Clojure gives you a few nice features, like immutable data structures, "nicer" reader syntax, and JVM compatibility. In exchange, you give up CLOS in favor of Java's broken object model.
All I'm saying is that I don't think the popular community is really considering CL's advantages before switching, they are only considering the disadvantages. If you are going to consider disadvantages, it's important to keep in mind that Clojure has them too.
(Another often-cited reason for switching to Clojure is Java's libraries. In my experience working with Java, its libraries are pretty bad. Read the URLConnection class that comes with Java 6. It is clear that the API designer has no idea what OOP is, and it's clear that those implementing the library had no idea how to program. CL libraries, I've found, are usually better-done.)
But with that in mind, I really like Clojure. I have the occasional client that insists on JVM applications, and Clojure is significantly less painful than pure Java (or JRuby, or Scala). But it is not my first choice when the JVM is not a requirement.