Here's my take on it.
(defn simplify-and
[[op & forms]]
(let [known-falsy? #(or (false? %) (nil? %))
known-truthy? #(and (not (symbol? %))
(not (seq? %))
(not (known-falsy? %)))
falsy-forms (filter known-falsy? forms)
unknown-forms (remove known-truthy? forms)]
(if (seq falsy-forms)
(first falsy-forms)
(case (count unknown-forms)
0 true
1 (first unknown-forms)
(cons op unknown-forms)))))
(comment (simplify-and `(and true 1 2 a)))
However, we can write a more generic simplify
that uses multimethods to simplify lists, so that we can add more optimisations without modifying existing code. Here's that, with optimisations for and
, or
and +
from clojure.core
. This simplify
only optimises lists based on namespace qualified names.
Check out the examples in the comment
form. Hope it makes sense.
(defn- known-falsy? [form]
(or (false? form) (nil? form)))
(defn- known-truthy? [form]
(and (not (symbol? form))
(not (seq? form))
(not (known-falsy? form))))
(declare simplify)
(defmulti simplify-list first)
(defmethod simplify-list :default [form] form)
(defmethod simplify-list 'clojure.core/and
[[op & forms]]
(let [forms (mapv simplify forms)
falsy-forms (filter known-falsy? forms)
unknown-forms (remove known-truthy? forms)]
(if (seq falsy-forms)
(first falsy-forms)
(case (count unknown-forms)
0 true
1 (first unknown-forms)
(cons op unknown-forms)))))
(defmethod simplify-list 'clojure.core/or
[[op & forms]]
(let [forms (mapv simplify forms)
truthy-forms (filter known-truthy? forms)
unknown-forms (remove known-falsy? forms)]
(if (seq truthy-forms)
(first truthy-forms)
(case (count unknown-forms)
0 nil
1 (first unknown-forms)
(cons op unknown-forms)))))
(defmethod simplify-list 'clojure.core/+
[[op & forms]]
(let [{nums true non-nums false} (group-by number? (mapv simplify forms))
sum (apply + nums)]
(if (seq non-nums)
(cons op (cons sum non-nums))
sum)))
(defn simplify
"takes a Clojure form with resolved symbols and performs
peephole optimisations on it"
[form]
(cond (set? form) (into #{} (map simplify) form)
(vector? form) (mapv simplify form)
(map? form) (reduce-kv (fn [m k v] (assoc m (simplify k) (simplify v)))
{} form)
(seq? form) (simplify-list form)
:else form))
(comment
(simplify `(+ 1 2))
(simplify `(foo 1 2))
(simplify `(and true (+ 1 2 3 4 5 foo)))
(simplify `(or false x))
(simplify `(or false x nil y))
(simplify `(or false x (and y nil z) (+ 1 2)))
)