Reloaded a protocol and "No implementation of method"?
While protocols provide a nice way to program to interfaces, they do have some unfortunate interactions with repl driven development. It’s annoying to be working along, reload a file, and then BOOM.
But this code was just working! My
Drawable. It’s right there. I can see it in the code!
Having code that looks like it should work, but getting runtime errors is annoying. Getting ones that aren’t immediately understandable, about code that was just working, can frustrate anyone.
So what causes this error?
To duplicate this error imagine working on two files.
Loading these files into a repl works. Calling
draw-many-rectangles outputs something expected.
Now imagine having to change what the output is. Perhaps instead of a list of strings
draw-many needs to return a single string.
A common workflow is to reload a file into a repl once editing it is done. This could be with cider’s
C-c C-x, or clojure’s
load-file functions. Doing this for the
z.draw namespace and attempting to run
(draw-many-rectangles) will cause an error.
This happens because when the
z.draw namespace was reloaded it created a new protocol with the same name. But the
z.core namespace was evaluated with the previous reference. It’s
defrecord referred to the old version of
Drawable. When creating a
Rectangle it will implement the old protocol.
How to fix
z.core namespace needs to be reloaded. Then it will redefine
Rectangle and refer to the correct
Drawable. This can be done by using the same load-file technique that loaded
Doing this by hand can be annoying. There are techniques to reduce the possibility of encountering this. One is to only define a protocol in a other wise empty namespace. All other functions go elsewhere. Then these types of errors can only occur when changing the protocol definition.
A tooling based solution is to use tools.namespace and something similar to reloadable systems in order to reload your entire application at once. I’ve been told cursive’s “Load File in REPL” command implements a similar namespace reloading mechanism.