 
 ; DESCRIPTION LENS
 
+(defn merge-meta-data [old-expr new-expr]
+  (putback (vary-meta new-expr #(merge (meta old-expr) %)) submorphs 
+           (map merge-meta-data
+                (concat (fetch old-expr submorphs) (iterate identity {})) 
+                (fetch new-expr submorphs))))
+
 (deflens description [expr new-description]
   :fetch 
   (with-out-str 
     (-> expr meta :description pprint))  
   :putback
-  (let [new-expr (if (string? new-description) (morph-eval-str (str "'" new-description)) new-description)] 
+  (let [new-expr (if (string? new-description) 
+                   (morph-eval-str (str "'" new-description)) 
+                   new-description)] 
     (if (morph? new-expr)
-      (vary-meta (morph-eval new-expr) #(merge (meta expr) %))
+      (merge-meta-data expr (morph-eval new-expr))
       (vary-meta (morph-eval new-expr) merge {:expanded-expression? true
                                               :description new-expr
+                                              :init-description new-expr
                                               :changes {}})))) 
 
 ; CHANGES LENS


(declare add-morph add-morph-before without set-prop)

(def LOOP_MAPPING true)

(defmulti changes->form (fn [expr changes opts] 
                          (if LOOP_MAPPING 
                            (first expr)
                            :default)))

; unpack the previously added changes (BUG: what if expression originally comes with a '-> ?)

(defmethod changes->form '-> 
  ([[_ expr _] changes opts]
   (changes->form expr changes opts)))

(defn modify-prototype [prototype-expr unbound-changes]
  (let [[self props & submorphs] prototype-expr]


; default expression change preserving

(defmethod changes->form* :default 
  ([expr {struct-changes :structure
          id->prop-changes :properties} _]
   (if (or (not-empty struct-changes) (-> id->prop-changes empty? not))
     (apply list '-> expr
       (apply concat struct-changes
         (for [[morph prop-changes] id->prop-changes]
           (concat (for [[prop value] prop-changes]
                     (list '=> morph prop value))))))
     expr)))

(defmulti apply-changes 
  "Applies the changes to the expression and also updates the change meta-data accordingly"
  (fn [expr changes] (ast-type expr)))

(defmethod apply-changes :morph [morph changes]
  (morph-eval (changes->form (-> morph meta :description) changes {})))

(defmethod apply-changes :expr [expr changes]
  (let [new-description (changes->form (-> expr meta :description) changes {:expanded-expression expr})
        new-expansion (morph-eval new-description)]
    (vary-meta new-expansion merge
      {:expanded-expression? true
       :init-description (expr meta :init-description)
       :description new-description})))

; SUBMORPH LENS
