portal

taryn 2025-03-05T22:02:49.900349Z

Hello! I am using Portal 0.23.0 in a Lein project with a custom submit function that assocs some data onto the meta of the tap>'d value (iff the value supports meta). At tap> callsites I am also associng data onto metas. The issue is, the meta at the tap callsite seems to be lost between calling tap> and submit's body.

(ns greet)

(defn hello
  [user]
  (-> user
      (doto #(tap> (vary-meta % assoc :foo :bar)))
      :username
      (str "Hello, ")))
(ns repl
  (:require '[portal.api :as portal]))

(defn nav*
  [coll k v]
  v)

(defn can-meta?
  [value]
  (instance? IObj x))

(defn nav
  [coll k v]
  (let [result (nav* coll k v)]
    (if-not (can-meta? result)
      v
      (vary-meta v assoc `nav #'nav))))

(defn submit
  [value]
  (portal/submit
    (if-not (can-meta? value)
      value
      (do (prn (meta value)) ;; the meta printed is *always* `nil`
          (vary-meta value assoc `nav #'nav)))))

(comment

    (require '[portal.api :as portal])
    (add-tap #'submit)

    (def p (portal/open))

    (require '[greet])
    (greet/hello (with-meta {:username "Johnny"}
                      {:qux :baz}))

    (remove-tap #'repl/p)

  nil)
After calling greet/hello I expect to see in portal the value {:username "Johnny"} with the meta {repl/nav #'nav, :foo :bar, :qux :baz} . But what I see is that value with meta {repl/nav #'nav}, the keys not assoc'd in repl/submit seem to be lost. Any guidance would be greatly appreciated. Thank you in advance!

djblue 2025-03-05T22:48:38.911039Z

I think one thing I noticed is the doto might be the issue:

(-> (with-meta {:username "Johnny"}
        {:qux :baz})
      (#(tap> (vary-meta % assoc :foo :bar))))
seems to behave more like what I expect, not sure why

djblue 2025-03-05T22:56:07.628999Z

Ohh, I think I see why:

(macroexpand
 '(-> (with-meta {:username "Johnny"}
        {:qux :baz})
      (doto #(tap> (vary-meta % assoc :foo :bar)))))
turns into:
(let*
 [G__100529 (with-meta {:username "Johnny"} {:qux :baz})]
 (fn*
  G__100529
  [p1__100526#]
  (tap> (vary-meta p1__100526# assoc :foo :bar))) 
 G__100529)
Which essentially only constructs the fn, but never invokes it

djblue 2025-03-05T22:58:02.577359Z

The code is essentially:

(let [a (with-meta {:username "Johnny"} {:qux :baz})]
  #(tap> (vary-meta % assoc :foo :bar)) ;; unused value
  a)

taryn 2025-03-06T12:55:44.441799Z

Thank you, Chris! I didn't think to use macroexpand to peek under-the-hood, but I will going forward. I have changed the (-> x (doto #(tap> (vary-meta % ,,,)))) forms to something like (-> x (#(do (tap> (vary-meta % ,,,)) %))) and it works exactly as desired.