How to require namespace inside function -main? - clojure

Suppose that in Leiningen project yo, I have these files:
foo.clj:
(ns yo.foo)
(def x 234.5)
bar.clj:
(ns yo.bar)
(def x -15)
And I have a main file (core.clj):
(ns yo.core)
(require '[yo.foo :as f])
(when (= f/x 234.5) (println "Succesfully required foo."))
(defn -main [& args]
(println (require '[yo.bar :as b]))
;(when (= b/x -15) (println "Succesfully required bar."))
)
When I enter "lein run" on the command line, I get this output:
Succesfully required foo.
nil
The first line tells me that I understand how to use the require function at the top level of a file. (Normally I would use :require in the ns statement.) The second line seems to indicate that I successfully required yo.bar.
However, when I uncomment the line containing when in -main, I get an exception:
java.lang.RuntimeException: No such namespace: b, compiling:(yo/core.clj:6:9).
Is there a way to perform a require from inside a function? My goal is to pass the name of a namespace on the command line, and have that namespace loaded as a result. Is there another way to do this? (I already know how to access command line arguments from within -main.)
(The problem is not that I wrapped the require call with println. I get the same exception if the first line of -main says only (require '[yo.bar :as b]).)
(The title of this question makes it seem as if it's the same as mine, but the question and the answer don't address the problem of requiring a namespace from inside a function.)

The require statement within the function -main is not invoked during compilation. Thus the compiler can't resolve the namespace b and complains.

Related

Programmatically redef functions

I'm currently in the start of making a tool that should modify all the functions in another namespace, and the run the "main" function in that (other) namespace. Almost like mocking in unit-testing, but for another purpose. The first step to this is to redefine functions from another namespace. The first listing shows code that works, where I explicitly name the function to be replaced.
(ns one)
(defn a-fun []
(println "original"))
(defn caller []
(a-fun))
(ns two)
(with-redefs [one/a-fun #(println "replaced")]
(one/caller))
;; replaced
;; nil
The next step is getting the functions programmatically from the other namespace. This is where I have run into a wall. I've experimented much with ns-publics, but so far no luck. So I try to break the problem down. And the problem is that the replacement of the function is not working. The output should print "replaced" not "original".
(ns two)
(def target (ns-resolve 'one (symbol "a-fun")))
(def caller (ns-resolve 'one (symbol "caller")))
(with-redefs [target #(println "replaced")]
(caller))
;; original
;; nil
Now, the #'two/target shows #'one/a-fun when I evaluate it in the repl. I can call one/a-fun by entering ((ns-resolve 'one (symbol "a-fun"))) in the repl, so I don't understand what part is not working.
I've been through a lot of documentation today, and I'm not really getting any closer.
You could try using with-redefs-fn like so:
(defn p [] "old")
(with-redefs-fn
{(ns-resolve *ns* 'p) #(println "new")}
(fn []
(p)))
;; => new
This does mean that the body of with-redefs-fn must be a function that uses your redefined Vars.

Clojure load-file on repl and call -main with arguments

I've done some coding in clojure inside core.clj which have a -main method that can take 0 or more arguments:
(defn -main
"dequeue function"
[& args]
I'm loading this clj file with:
(load-file "src/exercise/core.clj")
And then i'm trying to learn how to call this within repl in order to develop core_test.clj (if there is any tips about how to develop this auto tests, please give me some hints as well). What I'm trying to do now is:
(-main "resources\\sample-input.json" "a")
But this is printing "Arguments 0" which is a message that I told the code to print just to see how many arguments are being passed with
(println "Arguments" (count *command-line-args*))
How am I suposed to do this?
Thanks!
i'm trying to learn how to call this within repl in order to develop core_test.clj
Usually you'd write other functions that are called from -main and test those rather than the app's entry point -main.
But you should be able to call -main like any other function. I have a src/sandbox/main.clj file:
(ns sandbox.main)
(defn -main [& args]
(prn args))
Starting a REPL in the project folder, I can call -main like this:
(use 'sandbox.main) ;; (load-file "src/sandbox/main.clj") also works
=> nil
(in-ns 'sandbox.main)
=> #object[clojure.lang.Namespace 0x67ccce04 "sandbox.main"]
(-main "some" "args")
;; ("some" "args")
=> nil
There's a key difference in my example though: it's printing -main's args binding; *command-line-args* is nil because you're not running the code from the command line with args, you're running it from a REPL.
Regardless, it's probably a better idea to use an existing library to work with CLI args rather than *command-line-args* directly.

Using required namespace in the repl

When I require a namespace inside a clojure-script source file, I can use it afterwards in the code.
E.g:
(ns my.core
(:require [mylib.core :as lib]))
(lib/my-f)
(def something 99)
However, when I try to call (lib/my-f) inside the repl - after changing the namespace via (ns my.core) - I cannot access it. In contrast, all other definitions inside the ns are acessible: like something from the example above.
Is there a way to access the requirements in the repl? Or do I have to require them manually in the repl every time? This would be very tedious of course.
If you use ns to change namespace in a ClojureScript REPL, this sets the namespace aliases to match those used in the ns form.
Here is an example illustrating the concept:
$ clj -m cljs.main
ClojureScript 1.10.520
cljs.user=> (ns foo.core (:require [clojure.string :as string]))
foo.core=> (string/starts-with? "abc" "a")
true
foo.core=> (ns bar.core)
bar.core=> (ns foo.core)
foo.core=> (string/starts-with? "abc" "a")
WARNING: No such namespace: string, could not locate string.cljs, string.cljc, or Closure namespace "" at line 1 <cljs repl>
WARNING: Use of undeclared Var string/starts-with? at line 1 <cljs repl>
ReferenceError: "string" is not defined
If instead you use the in-ns REPL special to change to an existing namespace, this will preserve aliases:
$ clj -m cljs.main
ClojureScript 1.10.520
cljs.user=> (ns foo.core (:require [clojure.string :as string]))
foo.core=> (string/starts-with? "abc" "a")
true
foo.core=> (ns bar.core)
bar.core=> (in-ns 'foo.core)
nil
foo.core=> (string/starts-with? "abc" "a")
true
An interesting related aspect: If you use require, it will, under the hoods, employ an ns form with special meta baked into the form that preserves existing aliases:
$ clj -m cljs.main
ClojureScript 1.10.520
cljs.user=> (require '[clojure.string :as string])
nil
cljs.user=> (require '[clojure.string :as str])
nil
cljs.user=> (string/starts-with? "abc" "a")
true
cljs.user=> (str/starts-with? "abc" "a")
true
If you are curious, this is the :merge true meta here: https://github.com/clojure/clojurescript/blob/r1.7.228/src/main/clojure/cljs/repl.cljc#L679
and it is honored by the analyzer here: https://github.com/clojure/clojurescript/blob/r1.7.228/src/main/clojure/cljs/analyzer.cljc#L1953
By seeing how this works, it should provide some insight into why an ns form evaluated directly in the REPL (without merge meta) can lead to aliases being cleared.
In short, avoid directly using the ns special to change to a namespace in the REPL. Instead use it to create a new namespace in the REPL while specifying any required namespaces.
Use the in-ns REPL special to switch to an existing namespace. It can also be used to create a new namespace.
Use require to load namespaces into the REPL, and then use in-ns to switch to them.
As long as you require the namespace before switching to it with ns or in-ns, this all should work fine. The puzzling thing to me is that something is accessible, meaning your code was loaded: that should mean that its namespace form was evaluated too, and thus you should have its aliases available. Are you sure you did this from a fresh state, and didn't, say, define something independently as well? Double-check by:
Close the repl
Start a new repl
(require 'my.core)
(in-ns 'my.core)
Check that the stuff in your question is still true. Can you still access something? Can you still not access lib/my-f? I predict that one of those two things will change: you should be able to access neither, or both.

Clojure: (:refer-clojure :exclude [read]) apparently not working

I am having an issue with (:refer-clojure :exclude [read]). My setup is as follow:
I have a file foo.clj which is:
(ns foo.core)
(load "foo_bar")
Then I have a file bar.clj which is:
(ns foo.core
(:refer-clojure :exclude [read]))
(defn read [])
In my application, I did split a namespace in multiple file. This is why I have a series of (load) statements in the foo.clj file which is the entry point.
The problem is that when I compile this file, I am still getting the famous error:
WARNING: read already refers to: #'clojure.core/read in namespace: clj-osf.crud, being replaced by: #'clj-osf.crud/read
I don't think this would be much of a problem, but the issue is that when I use that library from another application, that other application won't compile and tell me what foo.core/read simply doesn't exist.
I also tried to change foo.clj for:
(ns foo.core
(:refer-clojure :exclude [read]))
(load "foo_bar")
But the same issue happens. Is this a bug in Clojure, or am I missing something?
I am using Clojure 1.6
It doesn't seem like the ns'es you are using match your file names. I'm not sure if that's just sloppy examples or if that's actually the issue.
Usually when you split a namespace across files, the loaded files should start with (in-ns 'foo.core), not (ns foo.core). clojure.pprint is a good example in core (it loads a bunch of sub files).
A fuller example:
foo/core.clj:
(ns foo.core
(:refer-clojure :exclude [read]))
(defn read [] "hi")
(load "bar")
foo/bar.clj:
(in-ns 'foo.core)
(defn read2 [] (str (read) " there"))
If I then start a repl:
user=> (require 'foo.core)
nil
user=> (foo.core/read)
"hi"
user=> (foo.core/read2)
"hi there"

ClassNotFoundException on use of another ns

As simple as this question is, I can't seem to find the right way for different namespaces in the same directory to validly refer to one another. I have two files:
project_root/src/babbler/core.clj:
(ns babbler.core
(:gen-class)
(use '[clojure.string :only (join split)]))
(defn foo [] "Foo")
and then project_root/src/babbler/bar.clj:
(ns babbler.bar)
(use [babbler.core :as babble])
This file also contains a main method, which is specified in my project.clj via :main babbler.bar
My entire structure is that generated by counterclockwise, with with default leiningen template.
The result of running lein repl is this:
Exception in thread "main" java.lang.ClassNotFoundException: babbler.core, compiling:(babbler/bar.clj:3:1)
at clojure.lang.Compiler.analyze(Compiler.java:6380)
at clojure.lang.Compiler.analyze(Compiler.java:6322)
at clojure.lang.Compiler$VectorExpr.parse(Compiler.java:3024)
at clojure.lang.Compiler.analyze(Compiler.java:6363)
at clojure.lang.Compiler.analyze(Compiler.java:6322)
(...)
Your use should be inside the definition of the namespace:
(ns babbler.bar
(use [babbler.core :as babble]))
In fact use is discouraged, you may want to write it as:
(ns babbler.bar
(:require [babbler.core :as babble :refer [foo]]))
That way you can call any function f from the babbler.core namespace as babble/f, and you can call foo directly. In addition, your file has information about where foo comes from so you or someone else won't need to go searching for it.