My definition of macro would be: a function that's evaluated at compile time.
In this sense, the idea of an "expression-macro" and a "pattern-macro" makes as much sense as an "expression-function" or "pattern-function". We don't typically have different classes of function to fulfil different purposes.
The `if-match` macro you describe could be written as a function, albeit with less pleasing syntax:
(defn if-match [pat expr then else]
(if-let [match (if (fn? pat) (pat expr) (isa? pat expr)]
(then match)
(else match)))
(if-match 0 0 (fn [_] true) (fn [_] false))
(if-match (pattern/cons 0 'x)
'(0 hi there)
(fn [{:syms [x]}] x)
(fn [_] nil))
So in the above case, we allow literal values to be passed in, or a function which returns a map of symbols to matching values. It's the function that allows for the extensibility.
A macro can easily smooth over the janky syntax of the `then` and `else` functions, and we'd end up something like this:
(if-match (pattern/cons 0 x)
'(0 hi there)
x
nil)
This is essentially what my previous example was alluding to, though in that I intended to use a namespaced macro rather than a namespaced function for extensibility.
You might well complain that `(pattern/cons 0 x)` is more verbose than `(cons 0 x)`, but it's also unambiguous, and doesn't require what would amount to a separate namespacing system, which a Lisp-1 like Clojure tends to reject.
Now, I don't think the above examples are magical in any way. The macro is a small improvement over the function, and the function isn't particularly complex. If higher-order functions are your idea of wizardry, then there's not much in Lisp that you wouldn't consider magic.
Maybe your objection just stems from the use of something like `p/cons` instead of just `cons`, while still desiring `cons` to be namespaced in some way. In which case, I'd suggest that what you're really asking is not 'can I have expression-macros and pattern-macros', but 'can I create new namespacing systems'.