I've defined a macro in func.clj:
(ns my-library.func)
(defmacro my-macro [arg]
`(identity ~arg))
and I want to bind it to a variable in core.clj:
(ns my-library.core
(:require [my-library.func :as func])
(def my-macro-core func/my-macro)
so that I can do something like the following:
(ns script.core
(:require [my-library.core :as mlc]))
(mlc/my-macro-core :boring-macro) ; -> :boring-macro
However, I can't do that because macros don't evaluate to a value. The following works:
(ns my-library.core
(:require [my-library.func :as func])
(defmacro my-macro-core [arg]
`(func/my-macro ~arg))
but it's redundant, so I was wondering if there's a cleaner way to do this.
It's a bit of a hack, but it can be done:
(ns my-library.func)
(defmacro my-macro [arg]
`(identity ~arg))
(ns my-library.core
(:require [my-library.func :as func]))
(def ^:macro my-macro-core ##'func/my-macro)
(ns script.core
(:require [my-library.core :as mlc]))
(mlc/my-macro-core :boring-macro)
The trick is to set the new var being defined to the raw function value of the macro being aliased, while setting the :macro metadata flag so the compiler recognizes it and calls it appropriately.
Potemkin's import-vars supports macros:
(require '[potemkin :refer [import-vars]])
(import-vars [my-library.core my-macro])
Related
I would like a macro this-ns such that it returns the namespace of the location where it is being called. For instance, if I have this code
(ns nstest.main
(:require [nstest.core :as nstest]))
(defn ns-str [x]
(-> x (.getName) name))
(defn -main [& args]
(println "The ns according to *ns*:" (ns-str *ns*))
(println "The actual ns:" (ns-str (nstest/this-ns))))
I would expect that calling lein run would produce this output:
The ns according to *ns*: user
The actual ns: nstest.main
What I came up with as implementation was the following code:
(ns nstest.core)
(defmacro this-ns []
(let [s (gensym)]
`(do (def ~s)
(-> (var ~s)
(.ns)))))
It does seem to work, but it feels very hacky. Notably, in the above example it will expand to def being invoked inside the -main function which does not feel very clean.
My question: Is there a better way to implement this-ns to obtain the namespace where this-ns is called?
here is one more variant:
(defmacro this-ns []
`(->> (fn []) str (re-find #"^.*?(?=\$|$)") symbol find-ns))
the thing is the anonymous function is compiled to a class named something like
playground.core$_main$fn__181#27a0a5a2, so it starts with the name of the actual namespace the function gets compiled in.
Can't say it looks any less hacky, then your variant, still it avoids the side effect, introduced by def in your case.
Interesting question. I would never have guessed that your code would output user for the first println statement.
The problem is that only the Clojure compiler knows the name of an NS, and that is only when a source file is being compiled. This information is lost before any functions in the NS are called at runtime. That is why we get user from the code: apparently lein calls demo.core/-main from the user ns.
The only way to save the NS information so it is accessible at runtime (vs compile time) is to force an addition to the NS under a known name, as you did with your def in the macro. This is similar to Sean's trick (from Carcingenicate's link):
(def ^:private my-ns *ns*) ; need to paste this into *each* ns
The only other approach I could think of was to somehow get the Java call stack, so we could find out who called our "get-ns" function. Of course, Java provides a simple way to examine the call stack:
(ns demo.core
(:use tupelo.core)
(:require
[clojure.string :as str]))
(defn caller-ns-func []
(let [ex (RuntimeException. "dummy")
st (.getStackTrace ex)
class-names (mapv #(.getClassName %) st)
class-name-this (first class-names)
class-name-caller (first
(drop-while #(= class-name-this %)
class-names))
; class-name-caller is like "tst.demo.core$funky"
[ns-name fn-name] (str/split class-name-caller #"\$")]
(vals->map ns-name fn-name)))
and usage:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[demo.core :as core]))
(defn funky [& args]
(spyx (core/caller-ns-func)))
(dotest
(funky))
with result:
(core/caller-ns-func) => {:ns-name "tst.demo.core", :fn-name "funky"}
And we didn't even need a macro!
There are some functions that read source code of a function like: source and source-fn.
Is there any way or function that returns source code of the Clojure file when a namespace is provided?
Such as: (all-source 'my-ns)
Returns such as:
(ns my-ns
(:require [kezban.core :refer :all]
[leiningen.c.util :as util]))
(defn my-fn
[]
)
...
I think I found a way(it works if the ns has at least one var):
(defn source-clj
[ns]
(require ns)
(some->> ns
ns-publics
vals
first
meta
:file
(.getResourceAsStream (RT/baseLoader))
IOUtils/toString))
I'm trying to build a small om-component that uses goog.fx.Dragger to make a target draggable. I require goog.fx.Dragger :as dragger and like so:
(ns om-draggable-me.core
(:require [om.core :as om :include-macros true]
[om.dom :as dom :include-macros true]
[goog.fx.Dragger :as dragger]))
When i use it like:
(defn draggable [app owner]
(reify
om/IDidMount
(did-mount [_]
(let [target (om/get-node owner)]
(dragger. target)))
om/IRender
(render [_]
(dom/div #js {:id "drag-box" :className "drag-box"}))))
I get the compiler warning:
WARNING: Use of undeclared Var om-draggable-me.core/dragger at line 22 src/cljs/om_draggable_me/core.cljs
Seen other examples of the use of goog.fx.DragDrop so why does not this work?
In your require form, dragger is a namespace alias
[goog.fx.Dragger :as dragger]
which is intended to be used like this:
dragger/your-var
;; the same as goog.fx.Dragger/your-var
while in your script, it was used as if it's a var:
(dragger. target)
and, as the compiler complained, such var hasn't been defined.
You can see people refer to clojure.string like this:
(require '[clojure.string :as str])
without worrying about conflicting with clojure.core/str function:
(str/join ", " [1 2 3])
(str "hello" "world")
For some reason the alias did not work, it works with fully qualified name:
(did-mount [_]
(let [target (om/get-node owner)]
(goog.fx.Dragger. target)))
I have the following clojure code (trying to write / get entries from redis):
(ns hello.core2)
(ns h hello.core2 (:require [taoensso.carmine :as car]))
(def pool (car/make-conn-pool))
(def spec-server1 (car/make-conn-spec))
(defmacro wcar [& body] `(car/with-conn pool spec-server1 ~#body))`
(defn -main
(wcar (car/ping)
(car/set "foo" "bar")
(car/get "foo")))
I get the following error:
Exception in thread "main" java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
at clojure.lang.RT.seqFrom(RT.java:487)
at clojure.lang.RT.seq(RT.java:468)
Any idea what im doing wrong?
(defn -main ...): you forgot the argument vector for the function -main; it should be (defn -main [& args] ...).
The problem is probably this:
(ns h hello.core2 (:require [taoensso.carmine :as car]))
There should be only one namespace declaration, like this (so also move the first ns expression):
(ns hello.core2
(:require [taoensso.carmine :as car]))
When I require libraries from the ns form I get :
test> (ns test (:require '(clojure.contrib [logging :as log] [sql :as sql]) ))
lib names inside prefix lists must not contain periods
[Thrown class java.lang.Exception]
When I use the require function it works as expected.
test> (require '(clojure.contrib [logging :as log] [sql :as sql]) )
nil
The documentation for ns refers to the documentation of the require function but as they behave differently this is a bit confusing.
The ns form is a macro, and so it doesn't require that you use ' to quote the provided seq.
An example from the Clojure docs:
(ns foo.bar
(:refer-clojure :exclude [ancestors printf])
(:require (clojure.contrib sql sql.tests))
(:use (my.lib this that))
(:import (java.util Date Timer Random)
(java.sql Connection Statement)))