Fork me on GitHub
#clojure-dev
<
2018-09-08
>
andy.fingerhut20:09:51

I was trying out tools.trace for the first time in a long time (latest version 0.7.9), which I believe works by replacing the value of vars whose values are functions, with wrapped functions that also print extra tracing messages.

andy.fingerhut20:09:27

When you try to use it to trace defprotocol methods, it seems to work as desired when those methods were associated with a type via extend-type, but not if those methods were given in a defrecord or deftype form. I haven't digested the implementation of defprotocol methods yet, but was looking for some hints about how this happens. It seems like when you use extend-type, the Var with the same name as the protocol method name has a value that is a function, and tools.trace can alter this. But for some reason this altering has no effect on the behavior of protocol method calls 'defined with' the type.

andy.fingerhut20:09:59

This is with Clojure 1.9.0 and latest tools.trace 0.7.9, in case it makes a difference.

andy.fingerhut20:09:18

I have demo code for various cases, and a (comment ...) section with lots of forms I evaluated in a REPL and comments showing whether I got trace messages or not, in this file: https://github.com/jafingerhut/demo-docs-tools.trace/blob/master/src/demo1/ns1.clj

gfredericks20:09:08

that's not surprising given my mental model of defprotocol

andy.fingerhut20:09:33

I'm happy to have your mental model become mine 🙂

gfredericks20:09:47

well a protocol has an associated interface

gfredericks20:09:07

and the Fast way to satisfy a protocol is to implement the interface; but that's only physically possible if you can affect the definition of the type

andy.fingerhut20:09:31

I didn't say, but all of this testing is on Clojure/Java, so OK there is a Java interface created when a protocol is. good.

gfredericks20:09:52

at the call site for a protocol function, I think there's a check if the object implements the interface, in which case it becomes a basic interface method call

andy.fingerhut20:09:32

So "the call site for a protocol function" somehow goes through a Clojure Var in some cases, but not others? That is the part where it is muddy for me.

gfredericks20:09:52

if the object implements the interface, it bypasses the var and just calls the interface method directly

gfredericks20:09:03

so tools.trace has no opportunity to intercept

andy.fingerhut20:09:09

The Clojure code that does the call isn't Java interop, it is (proto-method-name object), where there is a Var named proto-method-name.

andy.fingerhut20:09:58

I'm trying to figure out how changing the value of Var #'proto-method-name fails to affect the behavior of (proto-method-name object) in some cases, but does affect it in others. Maybe I need to go look at the implementation for protocols more.

gfredericks20:09:08

yes, but it compiles to the thing I described

gfredericks20:09:34

I think the compiler, when compiling (proto-method-name object), notices (at compile time) that it's a protocol function, and so emits the fancier bytecode

andy.fingerhut20:09:54

ok, if it is doing that, then I can understand the difference.

andy.fingerhut20:09:14

I should probably test with something like (apply proto-method-name object) to see if that makes a difference.

gfredericks20:09:26

I predict it would

andy.fingerhut20:09:41

thanks for the help

andy.fingerhut20:09:34

And your predictions are correct! Next I'd like to talk about the price of Tesla stock next week...

gfredericks21:09:20

well, I'm happy to help you with that using my mental model of defprotocol

andy.fingerhut21:09:42

Hmmm. So maybe at the call site, it is a Java method call, and the Var is a kind of 'default implementation' that is only used if the object doesn't implement that method?

andy.fingerhut21:09:08

Because it seems also to avoid going through the Var even if the type of the object is not known at compile time.

gfredericks21:09:19

yeah it has some kind of fancy runtime class cache

gfredericks21:09:59

my guess is it's effectively (memoize #(instance? TheInterface %))

gfredericks21:09:23

maybe without the memory leak features

andy.fingerhut21:09:32

I'd be curious to know if anyone knows of some library/tool like tools.trace, but that can also print trace messages for such calls. Maybe that would require some kind of JVM-level debugger?

gfredericks21:09:28

either that or something that fancily recompiles the relevant types

gfredericks21:09:31

which sounds pretty messy

alexmiller22:09:17

You can probably do such things with a Java agent

alexmiller22:09:20

You can hot swap classes

😮 1
alexmiller22:09:16

Really if you look for a Java thing to do this, it should work for protocols implemented in records/types

gfredericks22:09:42

but then probably would be less helpful for everything tools.trace is doing correctly