Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 286 lines (206 sloc) 10.84 kb
706c5f07 » Drew Colthorp
2010-04-25 readme
1 matchure
2 ============
3
4 Matchure is pattern matching for clojure.
5
6 * sequence destructuring
1a5f5f73 » Drew Colthorp
2010-05-01 move pattern compilation code to matchure/compile. Better documentati…
7 * map destructuring
706c5f07 » Drew Colthorp
2010-04-25 readme
8 * equality checks
9 * regexp matches
10 * variable binding
11 * instance checking
12 * arbitrary boolean expressions
13 * boolean operators (and, or, not)
4b6c0eab » Drew Colthorp
2010-04-27 First pass at fn-match and defn-match. needs cleanup and varargs tests.
14 * if, when, cond, fn, and defn variants
706c5f07 » Drew Colthorp
2010-04-25 readme
15
1a5f5f73 » Drew Colthorp
2010-05-01 move pattern compilation code to matchure/compile. Better documentati…
16 Matchure is pretty fast too - all patterns matches are compiled to nested if statements at compile time.
706c5f07 » Drew Colthorp
2010-04-25 readme
17
18 Usage
19 --------
20
21 ### Equality
22
23 Basic values check for equality
24
25 (if-match [nil nil] true) ;=> true
26 (if-match [1 1] true) ;=> true
27 (if-match ["asdf" "asdf"] true) ;=> true
28 (let [s "asdf"]
29 (if-match ["asdf" s] true)) ;=> true
30
31 ### Wildcards
32
33 `_` and `?` are both wildcards and will match anything. `_` is idiomatic for clojure.
34
35 `?` has special meaning in matchure. `?` can
36 be thought of as the thing being matched against, and so by itself always succeeds. It is also used to store the matched value in a variable
4b6c0eab » Drew Colthorp
2010-04-27 First pass at fn-match and defn-match. needs cleanup and varargs tests.
37 and is substituted into function calls for arbitrary tests (examples below).
706c5f07 » Drew Colthorp
2010-04-25 readme
38
39 ### Regular expressions
40
41 Regular expression literals check for a match
42
43 (if-match [#"hello" "hello world"] true) ;=> true
44
45 ### Type checking
46
47 Fully qualified class/interface names check `instance?`.
48
49 (if-match [java.lang.String "foo"] true) ;=> true
50 (if-match [java.lang.Comparable "foo"] true) ;=> true
51
52 ### Variable binding
53
54 The form `?var` is a pattern which always succeeds and has the side effect of binding the matched-against value to the variable `var`.
55
56 (if-match [?foo "bar"] foo) ;=> "bar"
57
58 ### Sequence destructuring
59
60 Literal vectors destructure and match sequences.
61
62 (if-match [[?fst & ?rst] [1 2 3]] [fst rst]) ;=> [1 (2 3)]
63 (if-match [[:message ?value] [:message "foo"]] value) ;=> "foo"
64 (if-match [[java.lang.String java.lang.String] (list "hello" "world")] true) ;=> true
65 (if-match [[[?a] ?b & ?rest] [[1] 2 3 4]] (list a b rest)) ;=> (1 2 (3 4))
66
67 ; can also destructure maps
68 (if-match [[[?key ?value] & ?rest] (sorted-map 1 2)] [key value rest]) ;=> (1 2 ())
69 (if-match [[[?key ?value] & ?rest] (sorted-map 1 2, 3 4)] [key value rest]) ;=> (1 2 ([3 4]))
70
71 Failing matches
72
73 (if-match [[?fst & ?rst] []] [fst rst] :failed-match) ;=> :failed-match
74 (if-match [[[?key ?value] & ?rest] (sorted-map)] [key value rest]) ;=> nil
75
76 ### Destructuring Maps
77
78 Map literals look up corresponding values by key and check the value of the given map against the pattern value of the pattern map.
79
80 (if-match [{:foo java.lang.String} {:foo "bar"}] true) ;=> true
81 (if-match [{:foo java.lang.String} (sorted-map :foo "bar")] true) ;=>
82
83 Keys that aren't pattern matched are ignored
84
85 (if-match [{:foo java.lang.String} {:foo "bar", :baz "qux"}] true) ;=> true
86
87 Assoc lists aren't currently supported
88
89 (if-match [{:foo java.lang.String} [[:foo "bar"]]] true) ;=> nil
90
91 ### Expressions
92
93 Lists are evaluated as clojure expressions, with `?` being substituted for the matched-against value. For example, to check for an odd integer, you would use
94
95 (if-match [(odd? ?) 1] true) ;=> true
96
97
98 ### Special forms
99
100 Not all lists are left as-is. Matchure has an extensible set of special forms. Right now, the special forms just include `quote` and boolean operators, `and`, `or`, and `not`.
101
102 One common use of `and` is to test a value and bind it to a variable if the test succeeds:
103
104 (if-match [(and ?foo #"hello") "hello world"] foo) ;=> "hello world"
105 (if-match [(and ?foo #"hello") "goodbye world"] foo) ;=> nil
106
107 Or and not also supported. To assert the matched value is a string either doesn't match `#"hello"` or matches both `#"hello"` and `#"world"`:
108
109 (if-match [(or (not #"hello") #"world") "hello world"] true) ;=> true
110 (if-match [(or (not #"hello") #"world") "whatever"] true) ;=> true
111 (if-match [(or (not #"hello") #"world") "hello everyone"] true) ;=> nil
112
113 #### Quote
114
1a5f5f73 » Drew Colthorp
2010-05-01 move pattern compilation code to matchure/compile. Better documentati…
115 Quote allows you to escape what would otherwise be a pattern so it's
116 tested for equality instead. For example, the pattern`'foo` tests for
117 equality to the symbol `foo`.
706c5f07 » Drew Colthorp
2010-04-25 readme
118
119 ### when-match and cond-match
120
121 You can also use `when-match`
122
123 (when-match [[?fst & ?rst] (list 1 2)]
124 (prn "asdf")
125 (prn "ghjkl"))
126
127 `cond-match` allows you to either test one value against multiple patterns
128
129 (cond-match "hello, world"
130 #"foo" "matches foo"
131 #"hello" "matches hello"
4b6c0eab » Drew Colthorp
2010-04-27 First pass at fn-match and defn-match. needs cleanup and varargs tests.
132 ? "doesn't match either") ;=> "matches hello"
706c5f07 » Drew Colthorp
2010-04-25 readme
133
134 Or match multiple values against multiple patterns
135
136 (let [s "hello world"]
137 (cond-match
138 [#"foo" s] "matches foo"
139 [#"hello" s] "matches hello"
4b6c0eab » Drew Colthorp
2010-04-27 First pass at fn-match and defn-match. needs cleanup and varargs tests.
140 [? s] "doesn't match either")) ;=> "matches hello"
141
142 ### fn-match and defn-match
143
144 `fn-match` and `defn-match` work like the corresponding builtins, but
145 match their patterns. They support both the (fn [& args] body) and (fn
146 ([& arglist1] body1)...) variants. An IllegalArgumentException is
2445980f » Drew Colthorp
2010-04-28 fixed some fn-match and defn-match arity bugs with varargs. Started some
147 raised if either the number of arguments does not fit any provided
148 pattern or the particular specified arguments do not fit any
1a5f5f73 » Drew Colthorp
2010-05-01 move pattern compilation code to matchure/compile. Better documentati…
149 pattern.
4b6c0eab » Drew Colthorp
2010-04-27 First pass at fn-match and defn-match. needs cleanup and varargs tests.
150
151 `fn-match` defines anonymous functions that pattern match on
152 arguments:
153
154 (fn-match this
155 ([0] 1)
156 ([1] 1)
157 ([?n] (+ (this (dec n)) (this (dec (dec n))))))
158
159 `defn-match` works similarly:
160
161 (defn-match fib
162 ([0] 1)
163 ([1] 1)
164 ([?n] (+ (fib (dec n)) (fib (dec (dec n))))))
706c5f07 » Drew Colthorp
2010-04-25 readme
165
1a5f5f73 » Drew Colthorp
2010-05-01 move pattern compilation code to matchure/compile. Better documentati…
166 `fn-match` and `defn-match` are intended to work as though the
167 provided arguments are matched against each provided pattern in
168 order. For example, consider the function
169
170 (defn-match example-fn
171 ([_ & _] :wildcard)
172 ([] :no-args)
173 ([1] :one)
174 ([:a :b] :a-b))
175
176 `fn-match` first tests its arguments against `[_ & _]`, which will
177 match any sequence having at least one element. It then tests its
178 arguments against `[]` which matches an empty sequence. finally `[1]`
179 and `[:a :b]` are tested. Because `[_ & _]` will match any non-empty
180 set of arguments, `example-fn` only has two return values in practice:
181 `:no-args` is returned when `example-fn` is called with no arguments,
182 and `:wildcard` is returned for any other set of arguments. This is
183 true even for `(example-fn 1)` because `[_ & _]` is tested first.
184
185 Performance Characteristics
186 -------------------
187
188 An efforts been made to have patterns compile down into efficient
189 code. One goal was to be able to use patterns in almost any context
190 without having to worry about overhead. In general, every set of
191 patterns compiles down into a tree of `if` and `let`
192 statements. Pattern matches short circuit, and redundant checks are
193 avoided. For example, when `[:a, :b, :c]` is compiled, you end up with
194 a code tree that checks the value matched against is seqable, then
195 that the first element is equal to `:a`, then the second is equal to
196 `:b`, etc. If any of these checks fails, the match short circuits and
197 the failure branch is taken.
198
199 Because of this, the success and failure branches passed to `if-let`
200 (which all the other macros compile down to) can be repeated a large
201 number of times. If they contain other macro calls, the whole thing
202 can generate into a large amount of code. For this reason, if either
203 the true or the false branch is going to be repeated more than once,
204 and that branch is not atomic, it is wrapped in an anonymous function
205 which is called any place that case can occur. This compramise can
206 make simple cases a tiny bit slower but hopefully avoids more drastic
207 consequences for complex use cases and increases the chance the
208 resulting function is below the JVM's inline limit.
209
210 `fn-match` and `defn-match` define multi-arity functions behind the
211 scenes and only test patterns that compatible with the provided number
212 of arguments. Furthermore, varargs are only used if varargs patterns
213 are provided, and then only for argument counts that don't match any
214 non-variadic patterns. In practice, this means matching functions that
215 use simple patterns are about as fast as the function you probably
216 would have written yourself.
217
218 As an example, here's the `fn` `example-fn` ends up expanding
219 to.
220
221 (clojure.core/fn
222 ([]
223 (matchure/cond-match
224 [] (do :no-args)
225 [] (throw (java.lang.IllegalArgumentException. "Failed to match arguments"))))
226 ([arg02655]
227 (matchure/cond-match
228 [_ arg02655 _ (list)] (do :wildcard)
229 [1 arg02655] (do :one)
230 [_ arg02655] (throw (java.lang.IllegalArgumentException. "Failed to match arguments"))))
231 ([arg02656 arg12657]
232 (matchure/cond-match
233 [_ arg02656 _ (list arg12657)] (do :wildcard)
234 [:a arg02656 :b arg12657] (do :a-b)
235 [_ arg02656 _ arg12657] (throw (java.lang.IllegalArgumentException. "Failed to match arguments"))))
236 ([arg02653 arg12654 & rest2651]
237 (matchure/cond-match (clojure.core/list* arg02653 arg12654 rest2651)
238 [_ & _] (do :wildcard)
239 _ (throw (java.lang.IllegalArgumentException. "Failed to match arguments")))))
240
241 Notice that that `[_ & _]` is not tested when no arguments are passed
242 in, and `[]` is not tested when any arguments are passed in, because
243 these patterns' arities don't match those cases. Also note that
244 variadic patterns introduce a small amount of overhead to test (to
245 combine the extra arguments into a sequence), so putting variadic
246 patterns toward the end of a function definition can reduce overhead.
247
706c5f07 » Drew Colthorp
2010-04-25 readme
248 ### More examples
249
250 For more examples, see the [tests](http://github.com/dcolthorp/matchure/blob/master/test/matchure_test.clj).
251
252 ## Installation
253
254 See [http://clojars.org/matchure](http://clojars.org/matchure).
255
256 ## Todo
257
758dccb2 » Drew Colthorp
2010-04-28 updated todo
258 * Add public extension mechanism.
706c5f07 » Drew Colthorp
2010-04-25 readme
259
260 ## License
261
88cc1654 »
2011-11-28 tweaked readme wording
262 Copyright (c) 2011 Drew Colthorp
706c5f07 » Drew Colthorp
2010-04-25 readme
263
264 Permission is hereby granted, free of charge, to any person obtaining a copy
265 of this software and associated documentation files (the "Software"), to deal
266 in the Software without restriction, including without limitation the rights
267 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
268 copies of the Software, and to permit persons to whom the Software is
269 furnished to do so, subject to the following conditions:
270
271 The above copyright notice and this permission notice shall be included in
272 all copies or substantial portions of the Software.
273
274 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
275 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
276 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
277 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
278 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
279 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
280 THE SOFTWARE.
281
282
d053da6f »
2011-11-28 updated readme
283 Author
284 ======
88cc1654 »
2011-11-28 tweaked readme wording
285 * Drew Colthorp (colthorp@atomicobject.com) and [Atomic Object](http://www.atomicobject.com/)
d053da6f »
2011-11-28 updated readme
286 * More Atomic Object [open source](http://www.atomicobject.com/pages/Software+Commons) projects
Something went wrong with that request. Please try again.