Rich Hickey
unread,Feb 4, 2008, 5:07:57 PM2/4/08Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Sign in to report message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to Clojure
Jealous of pattern matching - no more! (Unless you want a structural
switch statement, in which case I can't help you :)
I've added a prototype for abstract structural binding (as of SVN rev
646)
It's built on a destructuring let, temporarily called let*.
When passed only symbols as binding forms, as does normal let, let*
behaves exactly as let, i.e. it is a direct replacement.
In addition to taking symbols as binding forms, let* can also take
vectors and maps.
The vectors allow you to bind names to parts of _sequential_ things
(not just vectors!), like vectors, lists, seqs, strings, arrays, and
anything that supports seq.
The basic sequential form is a vector of symbols, which will be bound
to successive elements from the binding value. In addition, and
optionally, & followed by a symbol will cause that symbol to be bound
to the 'rest' of the sequence, i.e. that part not yet bound. Finally,
also optional, :as followed by a symbol will cause that symbol to be
bound to the entire binding value:
(let* [[a b c & d :as e] [1 2 3 4 5 6 7]] [a b c d e])
->[1 2 3 (4 5 6 7) [1 2 3 4 5 6 7]]
These forms can be nested:
(let* [[[x1 y1][x2 y2]] [[1 2] [3 4]]] [x1 y1 x2 y2])
->[1 2 3 4]
Strings too:
(let* [[a b & c :as str] "asdjhhfdas"] [a b c str])
->[\a \s (\d \j \h \h \f \d \a \s) "asdjhhfdas"]
Map binding forms allow you to bind names to parts of _associative_
things (not just maps!), like maps, vectors, string and arrays (the
latter three have integer keys). It consists of a map of symbol-key
pairs, each symbol being bound to the value in the binding value at
the key. In addition, and optionally, an :as key in the binding map
followed by a symbol will cause that symbol to be bound to the entire
binding value. Also optionally, an :or key in the binding map followed
by another map may be used to supply default values for some or all of
the keys if they are not found in the binding value:
(let* [{a :a, b :b, c :c, :as m :or {a 2 b 3}} {:a 5 :c 6}]
[a b c m])
->[5 3 6 {:c 6, :a 5}]
The new binding forms can be nested within one another arbitrarily,
allowing you to pull apart just about anything:
(let* [a 1
[b c [d e & f :as x] {h :h} & g] [2 3 [4 5 6] {:h 100 :foo 2}
7 8 9]
{j :j, k :k, i :i, [r s & t] :ivec :as m :or {i 12 j 13}} {:j
15 :k 16 :ivec [22 23 24 25]}]
[a b c d e f g h i j k r s t m x])
-> [1 2 3 4 5 (6) (7 8 9) 100 12 15 16 22 23 (24 25) {:ivec [22 23 24
25], :j 15, :k 16} [4 5 6]]
I've switched 'for' to expand to let*, so now it, too, can take vector
and map binding forms:
(for [[a b c] (map list (range 1 10) (range 11 20) (range 21 30))] [c
b a])
-> ([21 11 1] [22 12 2] [23 13 3] [24 14 4] [25 15 5] [26 16 6] [27 17
7] [28 18 8] [29 19 9])
In addition I've provided fn*, defn*, and defmacro* which let you do
the same thing with fn parameters:
(defn* foo
([a [b c] d] [a b c d])
([a [b c] d & r] [a b c d r]))
(foo 1 [2 3] 4 5 6 7)
->[1 2 3 4 (5 6 7)]
All of these ___* names are temporary. My inclination is to have the
destructuring versions replace their counterparts, but I want to get
some more experience and some feedback first.
If you are at all interested, please try this out.
Feedback welcome as always,
Rich