Use pattern matching carefully in Clojure and understand when `core.match` is clearer than ordinary data-oriented conditionals.
Pattern matching lets code branch on the shape of data rather than only on boolean tests. In Clojure, this is not a built-in core language feature in the same way it is in some ML-family languages, but libraries such as core.match make that style available when it materially improves clarity.
That last condition matters. Pattern matching is powerful, but it is not automatically clearer than cond, destructuring, multimethods, or explicit data dispatch. In Clojure, it should earn its place.
Pattern matching is most useful when the code needs to branch on structured input:
When the main question is “What shape of value do I have?” matching can be very expressive.
1(ns app.expr
2 (:require [clojure.core.match :refer [match]]))
3
4(defn eval-expr [expr]
5 (match expr
6 [:num n] n
7 [:add a b] (+ (eval-expr a) (eval-expr b))
8 [:mul a b] (* (eval-expr a) (eval-expr b))
9 :else (throw (ex-info "Unknown expression"
10 {:type ::unknown-expr
11 :expr expr}))))
This is a good fit because the data shape is the main driver of behavior. The cases are compact and the branching logic stays close to the structure it is interpreting.
core.match HelpsUse it when:
cond or caseThis often happens in interpreters, parsers, protocol adapters, and compiler-style transformations.
Do not reach for pattern matching by default. Many Clojure problems are clearer with:
case on one dispatch keycond for straightforward predicate branchingIf the match form is longer than the data logic it is trying to clarify, you are probably forcing it.
Pattern matching works best when the data model is already clear. It should not be used to compensate for inconsistent or messy shapes. If some expressions are vectors, some are maps, and some are ad hoc lists with shifting positions, the real problem is the data model itself.
That makes this a design lesson as much as a syntax lesson: matching is easiest when the program uses stable tagged data.
The first mistake is using pattern matching for simple cases that a map lookup or case would express more directly.
The second mistake is treating core.match as if it were mandatory to write functional code. It is just one tool.
The third mistake is forgetting about extensibility. Pattern matching is often strongest when the set of variants is known and fairly closed. If the system should grow through open extension, multimethods may be a better fit.
Ask these when reviewing a pattern-matching design:
cond, case, or destructuring be simpler?Good pattern matching makes structured data easier to read. Bad pattern matching turns normal branching into ornamental syntax.