Skip to content
Dustin Getz edited this page Nov 5, 2018 · 18 revisions

Matching Literals

The simplest thing you can do is match literals:

(let [x true
      y true
      z true]
  (match [x y z]
    [_ false true] 1
    [false true _ ] 2
    [_ _ false] 3
    [_ _ true] 4
    :else 5))
;=> 4

Note that the only clause that matches the values of the local variables is the fourth one. "Wildcards", the _, in the pattern signifies values that are present that you don't actually care about.

When matching on a single variable you may omit the brackets:

(let [x true]
  (match x
    true 1
    false 2
    :else 5))
;=> 1

Binding

You may match values and give them names for later use:

(let [x 1 y 2]
  (match [x y]
    [1 b] b
    [a 2] a
   :else nil))
;=> 2

This may seem pointless here but in complex patterns this feature becomes more useful (consider red black tree balancing for example).

Sequential types

You may match sequences by using the sequence matching facility:

(let [x [1 2 nil nil nil]]
  (match [x]
    [([1] :seq)] :a0
    [([1 2] :seq)] :a1
    [([1 2 nil nil nil] :seq)] :a2
    :else nil))
;=> :a2

Note this works on all ISeqs as well as Sequential types.

Vector types

You can also match vector types, the benefit is much better performance when you want to test something internal without looking at earlier values - random access:

(let [x [1 2 3]]
  (match [x]
    [[_ _ 2]] :a0
    [[1 1 3]] :a1
    [[1 2 3]] :a2
    :else :a3))
;=> :a2

core.match will optimize this case and test the third column first.

Rest patterns

Both seq and vector patterns support rest patterns. As in Clojure's builtin destructuring, rest pattern will capture the "rest" of a collection.

(let [x '(1 2)]
  (match [x]
    [([1] :seq)] :a0
    [([1 & r] :seq)] [:a1 r]
    :else nil))
;=> [:a1 (2)]

Map patterns

core.match supports matching maps. Here is a simple example:

(let [x {:a 1 :b 1}]
  (match [x]
    [{:a _ :b 2}] :a0
    [{:a 1 :b 1}] :a1
    [{:c 3 :d _ :e 4}] :a2
    :else nil))
;=> :a1

This will return :a1. Note that if you specify a key but you don't care about its value, you are asserting that the key must at least be present. For example:

(let [x {:a 1 :b 1}]
  (match [x]
    [{:c _}] :a0
    :else :no-match))
:=> :no-match

will return :no-match since the map does not have the key :c.

It's also useful to specify that some map has only a set of specified keys, this can be accomplished with the :only map pattern modifier:

(let [x {:a 1 :b 2}]
  (match [x]
    [({:a _ :b 2} :only [:a :b])] :a0
    [{:a 1 :c _}] :a1
    [{:c 3 :d _ :e 4}] :a2
    :else nil))
:=> :a0

This will return :a0 however the following:

(let [x {:a 1 :b 2 :c 3}]
  (match [x]
    [({:a _ :b 2} :only [:a :b])] :a0
    [{:a 1 :c _}] :a1
    [{:c 3 :d _ :e 4}] :a2
    :else nil))
;=> :a1

Will return :a1.

Or patterns

core.match supports "or" patterns - sugar for specifying alternatives.

(let [x 4 y 6 z 9]
  (match [x y z]
    [(:or 1 2 3) _ _] :a0
    [4 (:or 5 6 7) _] :a1
    :else nil))
;=> :a1

This is much more succinct that having to define six separate clauses.

Guards

core.match supports arbitrary guards on patterns:

(match [1 2]
  [(_ :guard #(odd? %)) (_ :guard odd?)] :a1
  [(_ :guard #(odd? %)) _] :a2
  :else :a4)
:=> :a2

Nesting

It is possible to match on nested maps:

(match [{:a {:b :c}}]
  [{:a {:b nested-arg}}] nested-arg)
;=> :c

Function application

core.match supports pattern matching on the result of function applications

(let [n 0]
  (match [n]
    [(1 :<< inc)] :one
    [(2 :<< dec)] :two
    :else :no-match))
;=> :one

The right hand side is the function to apply, the left hand side is any valid pattern.

Locals

Normally core.match will bind values to symbols, however it first tries to match against locals.

(let [String String
      Long Long]
  (match [(type 1)]    ; a java.lang.Class
         [Long] :long
         [String] :string))

Arrays (Experimental)

For performance reasons it may be useful to match on host arrays (Java, JavaScript). The vector matching logic can be specialized on host arrays. See Advanced Usage for a longer description.

Binary data (Experimental)

Similar to arrays it's useful to use the same vector matching syntax to match on binary data. Currently there is only a proof of concept in the repository. See Advanced Usage for a longer description.