I need to match two kinds of tuples and produce maps from them.
Both have a keyword and a string. One can have a third item (a language code).
[<key> <value>] ~> {:type <key> :value <value>}
[<key> <value> <lang>] ~> {:type <key> :value <value> :lang <lang>}
I only need to match those which keyword is either :foo or :bar and decided that I would use clojure.core.match:
(ns so.example
(:require
[clojure.core.match :refer [match]]))
(defn example-1 [ast]
(let [l10n-key #{:foo :bar}]
(match ast
[(k :guard l10n-key) v lang] {:type k :value v :lang lang}
[(k :guard l10n-key) v] {:type k :value v})))
(example-1 [:foo 10])
;=> {:type :foo, :value 10}
(example-1 [:bar 20 "en"])
;=> {:type :bar, :value 20, :lang "en"}
That works but I wanted to reuse the matching pattern :guard l10n-key in different clauses. So I thought I could use some syntax quoting and unquote splicing:
(defn example-2 [ast]
(let [l10n-key-match [:guard #{:foo :bar}]]
(match ast
[`(k ~@l10n-key-match) v lang] {:type k :value v :lang lang}
[`(k ~@l10n-key-match) v] {:type k :value v})))
However the defn expression crashes with:
Unexpected error (AssertionError) macroexpanding match at (form-init11111096422056977084.clj:3:5).
Invalid list syntax (clojure.core/concat (clojure.core/list (quote so.example/k)) l10n-key-match) in (clojure.core/seq (clojure.core/concat (clojure.core/list (quote so.example/k)) l10n-key-match)). Valid syntax: [[:default :guard] [:or :default] [:default :only] [:default :seq] [:default :when] [:default :as] [:default :<<] [:default :clojure.core.match/vector]]
What am I doing wrong?
Isn't this what spec, that already ships with Clojure, does? You would define your pattern like
We use
spec/defto define a spec,spec/catto concatenate specs andspec/?for a spec that is optional.Then we use
conformto parse the tuple: