Losing arguments with function wrappers for protocol methods?

I’m apparently losing parameters when destructuring when wrapping protocol method implementations with functions. Here’s an example:

(defprotocol F
  (-f [this a & r]))

(defn f [this a & r]
  (prn [:f {:this this :a a :r r}])
  (apply -f this a r))
  
(defrecord Fer []
  F
  (-f [this a & r]
    (prn [:-f {:this this :a a :r r}])
    {:a a :r r}))

(def fer (->Fer))
  
(f fer :x :p {:args :foo})
;; => {:a :x, :r {:args :foo}}
;; I'd expect this to be {:a :x, :r (:p {:args :foo})
;; Where'd the :p argument go?

;; stdout
;; [:f {:this #user.Fer{}, :a :x, :r (:p {:args :foo})}]
;; [:-f {:this #user.Fer{}, :a :x, :r {:args :foo}}]

For comparison, the :p argument doesn’t disappear when wrapping a function with a function:

(defn -g [this a & r]
  (prn [:-g {:this this :a a :r r}])
  {:a a :r r})

(defn g [this a & r]
  (prn [:g {:this this :a a :r r}])
  (apply -g this a r))

(g fer :x :p {:args :foo})
;; => {:a :x, :r (:p {:args :foo})}
;; Yay! Still have :p!

;; stdout
;; [:g {:this #user.Fer{}, :a :x, :r (:p {:args :foo})}]
;; [:-g {:this #user.Fer{}, :a :x, :r (:p {:args :foo})}]

Any ideas what’s going on? I’d expect the same behavior in both scenarios.

Ghadi Shayban answered in Slack: Protocols don’t support varargs. Thanks, Ghadi!

1 Like