This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-08-28
Channels
- # announcements (7)
- # beginners (49)
- # calva (18)
- # cider (48)
- # clj-yaml (1)
- # clojure (37)
- # clojure-dev (23)
- # clojure-europe (29)
- # clojure-nl (1)
- # clojure-norway (16)
- # clojure-sweden (41)
- # clojure-uk (6)
- # clojurescript (32)
- # community-development (9)
- # conjure (4)
- # datomic (13)
- # hyperfiddle (24)
- # kaocha (6)
- # leiningen (4)
- # missionary (46)
- # nextjournal (1)
- # nrepl (26)
- # off-topic (101)
- # shadow-cljs (67)
- # testing (9)
- # yamlscript (5)
i've been reading the code for defprotocol
and defrecord
and extend
recently, and I'm unsure how they intersect. defprotocol
generates a java interface and dynamically loads it (in genclass.clj
). defrecord
generates an interface as well using deftype*
, which is handled by Compiler.java. I only sort of follow what's happening, but it seems like the compiled record interface has defined methods for all of the implemented protocols. And then extend
just adds the implemented methods to the method cache on the protocol's var. how do these line up with each other?
A given object can satisfy a protocol by implementing an interface, by having the protocol extended to some super type of the object, or by adding some special metadata to an object
The metadata route you'll see if you look at how protocol callsites are compiled (and protocols have to opts into supporting)
The metadata route is the most dynamic, the interface route has the best performance
that makes sense. thanks. when compiling a protocol method, is it purely the compiler that determines which route to build?
I am not looking at it right now, so I don't recall if the inline cache covers all the cases, or just the extend case, and the jvm is expected to make the instance check for the interface fast
this is InvokeExpr
, right? i think i'm looking at the right spot
InvokeExpr has an emitProto
, I haven't seen a different one but this is a really big file and there's plenty to miss
no worries. this is really helpful, thank you
Yeah, I think I am muddling how the cache for keyword callsites work with the protocol sites
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L3741-L3743 is the interface check
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L3750-L3755 is the calling a protocol function (what does the extends dispatch logic)
the metadata bits must be tucked away inside the functions together with the extends stuff
here's my understanding leading up to that:
emit-protocol
defs (interns) the symbol names of the protocol methods with maps, including {:protocol (var ~name)}
(the protocol itself). at construction time, InvokeExpr
looks for that metadata and looks at the protocol's :on
to see if it's an actual Class. If it is, grab the :method-map
which is a map of the method names as keywords to the :on
class, get the class instance using the protocol method from the map, and store it on the InvokeExpr
object.
then in emitProto
as you noted, if the protocol actually implements the interface it emits an interface call, and if it doesn't it emits a "normal" clojure function call to the protocol var, which is the function produced by emit-method-builder
, where the method cache stuff happens
hell yeah, thank you for helping out, i don't think I would have put together how emitProto
works without your help