 (defn set-method [method]
   (SetMethod. method))
 
-(defn merge-result
-  [context match value]
-  (let [{:keys [pathsets] :as context} (update-context value (assoc context :pathsets []) match)]
-    [(dissoc context :pathsets) pathsets]))
-
 (defn merge-results
   [context results]
-  (loop [context context paths [] results results]
-    (if-let [[match value] (first results)]
-      (let [[context new-paths] (merge-result context match value)]
-        (recur context (into paths new-paths) (rest results)))
-      [context (->> paths
-                    (core/optimize (:graph context))
-                    core/collapse)])))
+  (letfn [(optimize-pathsets [{:keys [pathsets graph] :as context}]
+            (assoc context :pathsets (->> pathsets
+                                          (core/optimize graph)
+                                          core/collapse
+                                          (into []))))]
+    (let [context (reduce (fn [context [match value]]
+                            (update-context value context match))
+                          context
+                          results)
+          context (optimize-pathsets context)]
+      context)))
+
+(defn init-context
+  [method pathsets env]
+  {:method method
+   :pathsets pathsets
+   :env env
+   :graph {}
+   :request pathsets
+   :matched []
+   :optimized []
+   :missing []})
 
 (defn execute
-  [route-tree method runner pathsets]
-  (letfn [(match-and-run [{:keys [method] :as context} pathset]
+  [route-tree context runner]
+  (letfn [(context-result [context]
+            (dissoc context :method :pathsets :env))
+          (match-and-run [{:keys [method] :as context} pathset]
             (let [matches (match route-tree method pathset)
                   results (run context pathset matches runner)]
               results))
-          (execute* [context pathsets]
+          (execute* [{:keys [pathsets] :as context}]
             (lazy-seq
               (if (seq pathsets)
                 (let [results (mapcat (partial match-and-run context) pathsets)

                      results (for [[match values] results
                                    value values]
                                [match value])
                      [context paths] (merge-results context results)]
                  (cons (dissoc context :method)
                        (execute* context paths)))
                nil)))]
    (execute* {:method method
               :graph {}
               :paths []}
              pathsets)))

(defn gets [{:keys [route-tree]} pathsets ctx]
  (letfn [(runner [{:keys [handler]} path _]
            ((:get handler) path ctx))]
    (execute route-tree :get runner pathsets)))

(defn get
  ([router pathsets]
   (get router pathsets {}))
  ([router pathsets ctx]
   (last (gets router pathsets ctx))))

(defn sets [{:keys [route-tree]} pathmaps ctx]
  (let [{:keys [graph paths]} (graph/set {} pathmaps)
        ;; TODO: not sure if this is necessary
        paths (core/expand-pathsets paths)]
    (letfn [(runner [{:keys [method handler request virtual]} pathset execution-context]
              (case method
                :get ((:get handler) pathset ctx)
                :set (let [cache (:graph execution-context)
                           optimized-with-path (for [path paths
                                                     :let [optimized (first (core/optimize cache [path]))]

                                             {}
                                             optimized-with-path)
                           pathmap (:graph (graph/get set-graph [request]))]
                  ((:set handler) pathmap env))))]
      (let [pathsets (core/collapse paths)]
        (execute route-tree
                 (init-context :set pathsets env)
                 runner)))))

(defn set
  ([router pathmaps]
   (set router pathmaps {}))
  ([router pathmaps env]
   (last (sets router pathmaps env))))

(defn calls [{:keys [route-tree]} call-path args queries env]
  (letfn [(runner [{:keys [method handler request]} matched-path _]
            (case method
              :get ((:get handler) matched-path env)
              :call (let [results ((:call handler) matched-path args env)
                          ;; expand pathmaps so we can search for refs
                          results (into [] (mapcat (fn [result]
                                                     (if (core/pathmap? result)
