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
)
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 am trying to define a multimethod and its implementation in a separate file. It goes something like this:
In file 1
(ns thing.a.b)
(defn dispatch-fn [x] x)
(defmulti foo dispatch-fn)
In file 2
(ns thing.a.b.c
(:require [thing.a.b :refer [foo]])
(defmethod foo "hello" [s] s)
(defmethod foo "goodbye" [s] "TATA")
And in the main file when I am calling the method I define something like this:
(ns thing.a.e
(:require thing.a.b :as test))
.
.
.
(test/foo "hello")
When I do this I get an exception saying "No method in multimethod 'foo'for dispatch value: hello
What am I doing wrong? Or is it not possible to define implementations of multimethods in separate files?
It is possible. The problem is because thing.a.b.c namespace isn't loaded. You have to load it before using.
This is a correct example:
(ns thing.a.e
(:require
[thing.a.b.c] ; Here all your defmethods loaded
[thing.a.b :as test]))
(test/foo "hello")
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]
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)))