Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by
retagged by

Hi there, our code base have embraced extend-with-metadata protocols since its introduction - we like to have map components rather than records.

However, the code base and team are big clojure spec users and we were expecting the last form of the following to throw:

(ns defproto-spec.test
  (:require [clojure.spec.alpha :as s]
            [clojure.spec.test.alpha :as st]))

(defprotocol SpecTest
  :extend-via-metadata true
  (delete [impl id]))

(s/fdef delete-impl
  :args (s/cat :impl any? :id string?))

(defn delete-impl [_ id]
  (println (str "This deletes " id))

(def impl (with-meta {} {`delete delete-impl}))


(delete impl 1) ;; should throw but it doesn't

Why were we expecting it to work?

Maybe because the feature is newer (1.10) and because of the following:

defproto-spec.test> (st/instrument)
[defproto-spec.test/delete-me defproto-spec.test/delete-impl]

Unfortunately that's not that case. I would be willing to work on a patch/document this if is not already in someone else's pipeline.


1 Answer

+2 votes

I think you should hold on to the var like:

(def impl (with-meta {} {`delete #'delete-impl}))

or make another indirection using a wrapper function. Else instrumenting the var def-impl has no effect since the var already got dereferenced when you created impl.

Thank you @borkdude, let me try that out - did you find that written somewhere?

EDIT: it worked!

Would it be worth documenting it, if yes, where?
Maybe this helps:

user=> (def f (fn [] :foo))
user=> (def m {:f f})
user=> ((:f m))
user=> (alter-var-root #'f (constantly (fn [] :bar)))
#object[user$eval143$fn__144 0x31e72cbc "user$eval143$fn__144@31e72cbc"]
user=> ((:f m))
user=> (f)

As you can see, changing the root of var f does not have any effect on map f, since map does not contain a reference to the var anymore.
This is a bit of magic I had forgotten but I now see why it would work. Still - probably not trivial to grasp and worth documenting somewhere imho.