I'm using the following snipped to create a namespace, inject the given list of symbols and their values in the current namespace into the new namespace and switch to the new namespace:
(defn in-ns-with-vars
"Switch to a new namespace (creating it if necessary) and
inject the given symbols into the new namespace
with the values they resolve to in the current namespace."
[new-ns-symbol & symbols-to-inject]
(let [n (create-ns new-ns-symbol)
current-value-of (fn [v] #(ns-resolve (the-ns *ns*) v))]
(do
(doseq [v symbols-to-inject]
(intern new-ns-symbol v (current-value-of v)))
(in-ns new-ns-symbol)
(clojure.core/refer 'clojure.core))))
Is there an existing mechanism to achieve this? Due to usage in a scripting context the current (initial) namespace is statically unknown, so I cannot refer to it statically.
Instead of creating a new var pointing at the old var's value, you can use alias or refer with the captured symbol of the runtime *ns* to make the bindings visible in the newly created namespace.
user=> ((juxt identity type) (.name *ns*))
[user clojure.lang.Symbol]
The import-vars function from the potemkin library can do this for you (after using *ns* to reference your original namespace).
Related
I want to have a *flag* variable in a given namespace that is set to true only when :aot compiling.
Is there a way to do that?
Your issue is kind of complicated because the definition of Clojure's own dynamic nature, so there's no rough equivalent of C's #ifdef or some other mechanism that happens at compile time, but here's a workaround:
I created a Leiningen project with lein new app flagdemo. This trick detects when AOT is performed, as #Biped Phill mentioned above, using the dynamic var *compile-files*, and saves a resource in the classpath of the compiled code:
The code looks like this:
(ns flagdemo.core
(:gen-class))
(def flag-path "target/uberjar/classes/flag.edn")
(defn write-flag [val]
(try (spit flag-path (str val)) (catch Exception _)))
(defn read-flag []
(some-> (clojure.java.io/resource "flag.edn") slurp clojure.edn/read-string))
(write-flag false)
(when clojure.core/*compile-files*
(write-flag true))
(defn -main
[& args]
(println "Flag is" (read-flag)))
So, when the file loads using, say, lein run or when you load it in the REPL, it will try to write an EDN file with the value false.
When you compile the package using lein uberjar, it loads the namespace and finds that *compile-files* is defined, thus it saves an EDN file that is packaged with the JAR as a resource.
The function read-flag just tries to load the EDN file from the classpath.
It works like this:
$ lein clean
$ lein run
Flag is nil
$ lein uberjar
Compiling flagdemo.core
Created /tmp/flagdemo/target/uberjar/flagdemo-0.1.0-SNAPSHOT.jar
Created /tmp/flagdemo/target/uberjar/flagdemo-0.1.0-SNAPSHOT-standalone.jar
$ java -jar target/uberjar/flagdemo-0.1.0-SNAPSHOT-standalone.jar
Flag is true
Credit to #Biped Phill and #Denis Fuenzalida for explaining it to me.
I think this works as well:
(def ^:dynamic *static* false)
(when (and *compile-files* boot/*static-flag*)
(alter-var-root #'*static* true))
then in profiles.clj:
:injections [(require 'boot)
(intern 'boot '*static-flag* true)]
First you need to indicate that a variable will change over time:
(def ^:dynamic *the-answer* nil)
*the-answer*
;=> nil
The ^:dynamic bit will allow that variable to be rebound later:
(binding [*the-answer* 42] *the-answer*)
;=> 42
ADDENDUM 1
Here's what would happen if you didn't use ^:dynamic:
(def *the-answer* nil)
(binding [*the-answer* 42] *the-answer*)
;=> [...] Can't dynamically bind non-dynamic var [...]
ADDENDUM 2
These kind of variables are commonly referred to as "earmuffs". The naming convention is unenforced but strongly encouraged.
Here's what happen in the REPL when you use this naming without declaring a dynamic variable:
(def *the-answer* nil)
; Warning: *the-answer* not declared dynamic and thus is not dynamically rebindable, but its name suggests otherwise. Please either indicate ^:dynamic *the-answer* or change the name. (/tmp/form-init7760459636905875407.clj:1)
Trying to alias ns inside let to be able to use it locally, but got an error CompilerException java.lang.RuntimeException: No such namespace: sss when just trying to use alias
(ns core
(:require [clojure.set]
[clojure.string])
)
(let []
(alias 'sss 'clojure.string)
(println (ns-aliases *ns*) "hi1")
(println (sss/capitalize "hONdURas"))
;(println (clojure.string/capitalize "hONdURas")) ;;this works
(ns-unalias *ns* 'sss)
(+ 1 2)
)
(println (ns-aliases *ns*))
https://repl.it/repls/NoxiousRubberyComputationallinguistics
(alias ...) doesn't have to be top level. The way clojure works is that there is something called the reader that takes text data and turns it into data structures that are inputs to the compiler. See
https://clojure.org/reference/reader
Any namespaces referred to in the code have to already be defined for the reader prior to use. (Similarly for functions).
So, in
(let []
(alias 'sss 'clojure.string)
(println (sss/capitalize "aaa")))
the alias hasn't been assigned when the reader is trying to turn
(sss/capitalize)
into data.
I'm new to Clojure and for fun/education I'm writing a generic migration framework for lein. One thing this system has to do is read a clojure file from the disk, then run either the up function, or the down function. I figure this file should probably be evaluated in a temporary namespace but I'm having trouble getting that to work. Here's what I have so far:
(def user-namespace (create-ns 'leiningen.generic-migrate.user-eval))
(defn load-migration-file [file]
(binding [*ns* user-namespace]
(load-file (.getAbsolutePath file))
(keys (ns-publics *ns*))))
This gives me the error:
Unable to resolve symbol: defn in this context
My question is, what's the best approach for using load-file, then running a defined function, without risk of someone overwriting stuff in my namespace?
(defmacro with-ns
"Evaluates body in another namespace. ns is either a namespace
object or a symbol. This makes it possible to define functions in
namespaces other than the current one."
[ns & body]
`(binding [*ns* (the-ns ~ns)]
~#(map (fn [form] `(eval '~form)) body)))
(defmacro with-temp-ns
"Evaluates body in an anonymous namespace, which is then immediately
removed. The temporary namespace will 'refer' clojure.core."
[& body]
`(try
(create-ns 'sym#)
(let [result# (with-ns 'sym#
(clojure.core/refer-clojure)
~#body)]
result#)
(finally (remove-ns 'sym#))))
I'm currently reading the Clojure Programming book, and following the examples. However I come across an error.
(defn print-logger
[writer]
#(binding [*out* writer]
(println %)))
(def *out*-logger (print-logger *out*))
(*out*-logger "hello")
Will result in: `Can't dynamically bind non-dynamic var: user/out
I'm very new to Clojure, and don't understand why this happens, especially when I'm following the example :)
The issue is not in the code sample you have.
*out* should refer to clojure.core/*out*, so if you are running this in a repl you might have run something previous, to what you have, like (def *out* something) to create a user/*out*.
Then, when you defined your print-logger function, the (binding [*out* writer] ...) statement would be trying to rebind user/*out* instead of clojure.core/*out*.
You can use ns-unmap to remove user/*out* from your namespace.
(ns-unmap 'user '*out*) ;; => nil
You will also need to define your print-logger function again to recapture the correct clojure.core/*out*.
I have a Clojure program that I build as a JAR file using Maven. Embedded in the JAR Manifest is a build-version number, including the build timestamp.
I can easily read this at runtime from the JAR Manifest using the following code:
(defn set-version
"Set the version variable to the build number."
[]
(def version
(-> (str "jar:" (-> my.ns.name (.getProtectionDomain)
(.getCodeSource)
(.getLocation))
"!/META-INF/MANIFEST.MF")
(URL.)
(.openStream)
(Manifest.)
(.. getMainAttributes)
(.getValue "Build-number"))))
but I've been told that it is bad karma to use def inside defn.
What is the Clojure-idiomatic way to set a constant at runtime? I obviously do not have the build-version information to embed in my code as a def, but I would like it set once (and for all) from the main function when the program starts. It should then be available as a def to the rest of the running code.
UPDATE: BTW, Clojure has to be one of the coolest languages I have come across in quite a while. Kudos to Rich Hickey!
I still think the cleanest way is to use alter-var-root in the main method of your application.
(declare version)
(defn -main
[& args]
(alter-var-root #'version (constantly (-> ...)))
(do-stuff))
It declares the Var at compile time, sets its root value at runtime once, doesn't require deref and is not bound to the main thread. You didn't respond to this suggestion in your previous question. Did you try this approach?
You could use dynamic binding.
(declare *version*)
(defn start-my-program []
(binding [*version* (read-version-from-file)]
(main))
Now main and every function it calls will see the value of *version*.
While kotarak's solution works very well, here is an alternative approach: turn your code into a memoized function that returns the version. Like so:
(def get-version
(memoize
(fn []
(-> (str "jar:" (-> my.ns.name (.getProtectionDomain)
(.getCodeSource)
(.getLocation))
"!/META-INF/MANIFEST.MF")
(URL.)
(.openStream)
(Manifest.)
(.. getMainAttributes)
(.getValue "Build-number")))))
I hope i dont miss something this time.
If version is a constant, it's going to be defined one time and is not going to be changed you can simple remove the defn and keep the (def version ... ) alone. I suppose you dont want this for some reason.
If you want to change global variables in a fn i think the more idiomatic way is to use some of concurrency constructions to store the data and access and change it in a secure way
For example:
(def *version* (atom ""))
(defn set-version! [] (swap! *version* ...))