         (orig-dispatch o))
       (pprint obj))))
 
-(deftest ^:examples print-op-result
-  (ppm (gen/sample operation)))
+(defn alias
+  [prev-aliases]
+  (let [alias-gen (if (seq prev-aliases)
+                    (gen/elements prev-aliases)
+                    string) ;; avoid throwing when empty
+        alias-name (gen/such-that #(and (seq %)
+                                        (Character/isLetter (first %))
+                                        (not-any? (fn [e] (= % (:value e)))
+                                                  prev-aliases))
+                                  (gen/resize 20 gen/string-alpha-numeric))
+        expr (expression alias-gen)]
+    (->> (gen/tuple alias-name expr)
+         (gen/fmap (fn [[alias-name expr]]
+                     {(with-meta {:type :symbol :value alias-name}
+                        {:result (-> expr meta :result)})
+                      expr})))))
+
+(defn aliases
+  [size]
+  (if (zero? size)
+    (alias nil)
+    (gen/bind (gen/resize (dec size)
+                          (gen/sized aliases))
+              (fn [alias-map]
+                (gen/fmap
+                 (fn [generated-alias]
+                   (merge alias-map generated-alias))
+                 (alias (-> alias-map keys)))))))

  [or-op cat-op star-op plus-op opt-op not-op])

(def operation
  (gen/sized
   (fn [size]
     (let [op-generators (map #(gen/resize size %) all-op-generators)]
       (gen/one-of op-generators)))))

(defn ppm [obj]
  (let [orig-dispatch pprint/*print-pprint-dispatch*]

(defn use-alias [aliases]
  (gen/elements aliases))

(def collection-generators
  (map #(gen/resize 20 %) [string char-set]))

(declare operation)

(defn- operation-generator
  [{:keys [op max-args type-fn such-that]
    :or {max-args 4 such-that (constantly true)}}]
  (fn [alias-gen]
    (gen/sized
     (fn [size]
       (let [smaller-ops (gen/resize (dec size) (operation alias-gen))
             arg-gen (cond-> (conj collection-generators alias-gen)
                             (pos? size)
                             ;; v- hack to get more nested ops
                             (conj smaller-ops smaller-ops smaller-ops))]
         (->> (gen/vector (gen/one-of arg-gen) 1 max-args)
              (gen/fmap
               (fn [args]
                 (with-meta {:type :op, :value op, :args args}
                   {:result (type-fn (map #(-> % meta :result) args))})))
              (gen/such-that such-that)))))))

(def or-op
  (operation-generator
