That’s an interesting observation, but the big difference is that use in Clojure does not make the vars that you referred available to other namespaces using your namespace.
A framework calls you. So it needs something to call. You could imagine something like:
(defn invoke[opts]
;; Setup a bunch of stuff
(render opts) ; Call the render fn of this namespace
;; Do some framework related cleanup stuff
)
The framework might call invoke on namespaces that are supposed to be Views for example. But by default you might want to do some pre or post processing to this, and maybe there’s other things the framework might call, like a render-static and what not.
With Clojure’s use, you can get an invoke alias, but it isn’t itself callable from something that is using your own namespace, and it also would not be calling the render function inside your own namespace, but instead the one inside its own namespace.
Maybe a combination of use and potemkin’s import-vars would be a bit more similar, but even in that scenario, the “imported” invoke will not be calling your own render function.
Let’s try it:
(ns view)
(defn render[]
"Hello World!")
(defn invoke[]
(println "Calling render inside view")
(println (render)))
(ns myview
(:require [view :refer :all]))
(defn render[]
"Welcome to my view!")
(ns framework)
(myview/invoke)
;;=> java.lang.RuntimeException: No such var: myview/invoke
;;=>clojure.lang.Compiler$CompilerException: Syntax error compiling at (1:1)
So it didn’t really inherit invoke, it just is able to use it internally without qualifying it. And even if we try to call invoke from inside myview:
(ns myview
(:require [view :refer :all]))
(defn render[]
"Welcome to my view!")
(invoke)
;;=> Calling render inside view
;;=> Hello World!
;;=> nil
We don’t get the correct behavior, invoke has not called our “overriden” render function, but instead is calling view/render. So we see “Hello World!” instead of our own render message.
Where-as:
(ns view
(:require [inheritance :refer [binded-quote]]))
(defmacro __inherit__[& {:as opts}]
(binded-quote
[current-ns-name (str *ns*)
debug (or (:debug opts) false)]
(do
(defn render[]
"Hello World!")
(defn invoke[]
(when debug
(println "Calling render inside" current-ns-name))
(println (render))))))
(ns myview
(:require [inheritance :refer [inherit]]))
(inherit view :debug true)
(defn render[]
"Welcome to my view!")
(ns framework)
(myview/invoke)
;;=> Calling render inside myview
;;=> Welcome to my view!
;;=> nil
Here it properly inherited invoke, and the inherited invoke is correctly calling the render of myview, and not that of view, and on top of that the macro allows us a few cool things, like using the myview namespace name dynamically to template the code, which is how the framework can automatically print the name of each views as it invokes them. It also could have gone and registered this as a view with the framework so the framework knows to call it if you wanted, and it lets you customize the behavior with options, such as turning on debug for this view.
That’s interesting. Inheritance but at the level of the function body, maybe there’s use cases to that as well, though I’m not as sure for a framework, I guess it could let you extend an existing function in some ways, could this be used instead of middlewares?