How to unload a function from another namespace? - clojure

I load a function say-hi from namespace learning.greeting
(use 'learning.greeting)
When I try to re-defn the say-hi function under the current (user) namespace, I got the error:
CompilerException java.lang.IllegalStateException: say-hi already refers to: #'learning.greeting/say-hi in namespace: user, compiling:(NO_SOURCE_PATH:1:1)
So how to unload the function from other namespaces?

If you want to get rid of a direct mapping to a Var from another namespace at the REPL, say
(ns-unmap 'current-namespace 'local-alias)
Example:
user=> (ns-unmap *ns* 'reduce)
nil
user=> (reduce + 0 [1 2 3])
CompilerException java.lang.RuntimeException: Unable to resolve symbol: reduce in this context, compiling:(NO_SOURCE_PATH:2:1)
Local alias will differ from the actual name of the Var if :rename was used:
(use '[clojure.walk
:only [keywordize-keys]
:rename {keywordize-keys keywordize}])
To remove all mappings pointing at Vars in clojure.walk:
(doseq [[sym v] (ns-map *ns*)]
(if (and (var? v)
(= (.. v -ns -name) 'clojure.walk))
(ns-unmap *ns* sym)))

Do you really want to remove say-hi from learning.greeting? If not, it might be better to use require in this situation. Instead of (use 'learning.greeting), execute:
(require `[learning.greeting :as lg])
Then you can refer to the original definition as lg/say-hi, and you can define a new version in the current namespace, e.g. as
(def say-hi [x] (lg/say-hi (list x x))
(I don't know whether that makes sense for the say-hi function, but the general point is the same regardless.)

both use and require have an :exclude parameter for just this situation:
(use '[learning.greeting :exclude [say-hi]])
or more preferably use require:
(require '[learning.greeting :refer :all :exclude [say-hi]])
or when you are working in a normal namespace putting all this in the ns form is preferred:
(ns my-namespace
(:require [learning.greeting :refer [ function1 function2] :as greeting]

Related

Getting the namespace where a form is called

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!

Clojure - same function name different namespace

I am trying to do the following:
(ns ns-test.core
(:use [ns-test.a :as a]
[ns-test.b :as b]))
(def test-map {:key "a"})
(defmulti print-ns :key)
(defmethod print-ns "a" [input-map]
(a/foo input-map))
(defmethod print-ns "b" [input-map]
(b/foo input-map))
with namespaces a and b that look like this:
(ns ns-test.a)
(defn foo [x]
(println x "I'm in namespace A."))
and
(ns ns-test.b)
(defn foo [x]
(println x "I'm in namespace B."))
but when I try to load these classes into the REPL, I get this:
user=> (use 'ns-test.core :reload)
CompilerException java.lang.IllegalStateException: foo already refers to: #'ns-test.a/foo in namespace: ns-test.core, compiling:(ns_test/core.clj:1:1)
Why does this conflict between a/foo and b/foo exist, and how can I prevent it? (Isn't the whole point of namespaces and namespace qualification to allow me to have two different functions of the same name?)
You probably wanted to :require the namespaces a and b instead of :use. :use interns the namespace symbols to the current namespace, thus the conflict.

clojure - conj doesn't seem to add value to vector

I have written a function which takes a directory as input and returns a list of files.
(ns musicdb.filesystem)
(import '(java.io.File) '(java.net.url) '(java.io))
(use 'clojure.java.browse)
(require '[clojure.string :as str])
(defn getFiles
"get a list of all files"
[searchPath]
(def directory (clojure.java.io/file searchPath))
(def files (file-seq directory))
(def fonly (filter (fn [x]
(. x isFile)) files))
(def names [])
(doseq [x fonly]
(conj names (. x toString)) ;doesn't seem to work
(println (. x toString))) ;but this DOES print the file path
names)
The only thing that doesn't work here, is the conj call.
Here is my test
(ns musicdb.core-test
(:require [clojure.test :refer :all]
[musicdb.core :refer :all]
[musicdb.filesystem :refer :all]))
(deftest test_0
(testing "getFiles returns valid result"
(is (> (count (getFiles "/home/ls/books/books")) 1))
(doseq [i (take 5 (getFiles "/home/ls/books/books"))] (searchBook i))))
This test fails and shows that the return value of getFiles is empty.
names is an immutable vector. (conj names (. x toString)) creates a new vector but doesn't do anything with it. There are other problems with your code:
you don't want to use doseq. It's for side effects, such as printing things out. If you're creating a collection you usually don't need to iterate in clojure, or if you do you can use an immutable accumulator, loop and recur.
You don't want to use nested defs. You're defining globals, and what you want are function locals. Use let instead.
The clojure naming style is to use dashes instead of camel case (minor, just a convention).
You don't seem to be using your java.io importa in this code.
use in general is not a good idea, unless you restrict it to a few explicitly named functions with :only. This is to avoid confusion when looking at an unqualified name in your code, because you wouldn't know where it came from.
You want something like this:
(defn get-files [search-path]
(let [directory (clojure.java.io/file search-path)
files (file-seq directory)
fonly (filter #(.isFile %) files)]
(map #(.toString %) fonly)))

Determine namespace of a function's caller

I'm trying to accurately determine the namespace of a function's caller. Looks like *ns* is determined by the namespace at top of the call stack.
user=> (ns util)
nil
util=> (defn where-am-i? [] (str *ns*))
#'util/where-am-i?
util=> (ns foo (:require [util]))
nil
foo=> (util/where-am-i?)
"foo"
foo=> (ns bar)
nil
bar=> (defn ask [] (util/where-am-i?))
#'bar/ask
bar=> (ask)
"bar"
bar=> (ns foo)
nil
foo=> (util/where-am-i?)
"foo"
foo=> (bar/ask)
"foo"
foo=>
Is there some other meta data I can rely on or do I need to specify this manually?
This is not possible. In the repl, *ns* is always set to the namespace in which the repl is; at runtime it is usually clojure.core, unless someone goes to the trouble to set it, which is uncommon.
I'm not sure what your complete use case is, but judging from your example you want #'bar/ask to return its own namespace instead of returning a function that resolves the current namespace. You could simply use def instead of defn. Here is an example following on from what you did:
util=> (in-ns 'bar)
#<Namespace bar>
bar=> (def tell (util/where-am-i?))
#'bar/tell
bar=> (in-ns 'foo)
#<Namespace foo>
foo=> (refer 'bar :only '[tell])
nil
foo=> tell
"bar"
Hope this helps!

Calling Function In Separate Clojure Namespace

How do I call a function in one Clojure namespace, bene-csv.core from another namespace, bene-cmp.core? I've tried various flavors of :require and :use with no success.
Here is the function in bene-csv:
(defn ret-csv-data
"Returns a lazy sequence generated by parse-csv.
Uses open-csv-file which will return a nil, if
there is an exception in opening fnam.
parse-csv called on non-nil file, and that
data is returned."
[fnam]
(let [ csv-file (open-csv-file fnam)
csv-data (if-not (nil? csv-file)
(parse-csv csv-file)
nil)]
csv-data))
Here is the header of bene-cmp.core:
(ns bene-cmp.core
.
.
.
(:gen-class)
(:use [clojure.tools.cli])
(:require [clojure.string :as cstr])
(:use bene-csv.core)
(:use clojure-csv.core)
.
.
.
The calling function -- currently a stub -- in (bene-cmp.core)
defn fetch-csv-data
"This function merely loads the two csv file arguments."
[benetrak-csv-file gic-billing-file]
(let [benetrak-csv-data ret-csv-data]))
If I modify the header of bene-cmp.clj
(:require [bene-csv.core :as bcsv])
and change the call to ret-csv-data
(defn fetch-csv-data
"This function merely loads the two csv file arguments."
[benetrak-csv-file gic-billing-file]
(let [benetrak-csv-data bcsv/ret-csv-data]))
I get this error
Caused by: java.lang.RuntimeException: No such var: bcsv/ret-csv-data
So, how do I call fetch-csv-data?
Thank You.
You need to invoke the function, not just reference the var.
If you have this in your ns:
(:require [bene-csv.core :as bcsv])
Then you need to put parentheses around the namespace/alias qualified var to invoke it:
(let [benetrak-csv-data (bcsv/ret-csv-data arg)]
; stuff
)