   (fv [this])
   (ev [this dict]))
 
-(defn- fv- [pars expr & exprs]
-  (loop [exprs exprs  vars (transient (fv expr))]
-    (if-let [[expr & exprs] exprs]
-      (recur exprs (reduce conj! vars (fv expr)))
-      (persistent! (reduce disj! vars pars)))))
+(defn- ff [expr pars]
+  (-> (reduce disj (fv expr) pars)
+      (F. expr (vec pars))))
+
+(defn- aa [expr args]
+  (-> (reduce into (fv expr) (map fv args))
+      (A. expr (vec args))))
 
 (extend-protocol Expr
   F

;; https://ncatlab.org/nlab/show/pure+type+system

(defrecord V [name])            (defn v? [x] (instance? V x))
(defrecord A [vars form])       (defn a? [x] (instance? A x))
(defrecord F [vars expr pars])  (defn f? [x] (instance? F x))
;; vars : Set V
;; pars : Vec V
;; form : Vec Expr

(defprotocol Expr
  "an expression satisfies the following protocol.

        ;; F without pars evals to its expr
        (empty? pars) expr
        ;; uncurries immediately nested F
        (f? expr) (ff (:expr expr) (into pars (:pars expr)))
        :else     (ff        expr        pars))))
  A
  (fv [this] (:vars this))
  (ev [{:keys [vars expr args]} dict]
    (let [dict (select-keys dict vars)
          mbev #(cond-> % (some (fv %) (keys dict)) (ev dict))
          expr (mbev expr)]
      (if (f? expr)
        ;; asap beta-reduces val/var args
        (let [{:keys [pars expr]} expr
              ;; todo
              ;; - eval args in parallel
              ;; - skip eval if par not in fv
              ;; - skip eval if par is overwritten
              [args pars dict]
              , (loop [args (seq args)  args' []
                       pars (seq pars)  pars' []
                       dict {}]
                  (if-let [[arg & args] args]
                    (let [arg (mbev arg)]
                      (if-let [[par & pars] pars]
                        (if (or (v? arg) (empty? (fv arg))) ; todo or when par is used only once in expr
                          (recur args       args'      pars       pars'      (assoc  dict par arg))
                          (recur args (conj args' arg) pars (conj pars' par) (dissoc dict par)))
                        [(into (conj args' arg) (map mbev args)) pars' dict]))
                    [args' (into pars' pars) (reduce dissoc dict pars)]))
              expr (-> (ff expr pars)
                       (ev dict))]
          (if (seq args)
            (aa expr args)
            expr))
        ;; defers application or applies primitive expr
        (let [args (pmap mbev args)]
          ((if (some (comp seq fv) (cons expr args)) aa apply)
           expr args)))))
  V
  (fv [this] #{this})
  (ev [this dict] (dict this this))
