Take I have a completely separate (as in, somewhere in the file system) file:
(ns separate)
(def a "test")
Now, if I try to load that file like this:
(load-file "separate.clj")
(require 'separate)
(separate/a)
It throws an error saying that separate isn't found as a namespace. Why is this and how do I fix it?
EDIT: This is literally the code I'm running:
(require 'separate)
Should be:
(require '[separate])
There is no example in clojuredocs probably because single part namespaces are unusual.
Found the solution, I need to do something like this:
(deref (get (ns-interns 'separate) 'a))
Related
I have the following clojure code to initialize my config structure Config.
I noticed that the file is actually read when compiling the file, not at runtime.
For me, the config structure Config should be immutable, however, I do not want to have the configuration inside the JAR file.
How should I do this? Do I have to use an atom? It is okay if the application crashes if my.config is missing.
(def Config
(read-string (slurp "my.config")))
When you don't want it at compile time you have to wrap it in a function.
(defn def-my-conf []
(def Conf (blub)))
But you the cleaner way would be:
(declare Config)
(defn load-Config []
(alter-var-root (var Config) (blub)))
This function should be called inside your main.
EDIT: Of course an atom is also a solution!
Write a function for reading your config:
(defn read-config
[]
(read-string
(slurp "my.config")))
Then you can call this function from -main, and either 1) pass the config on to any functions that will need it, or 2) store it in a dynamic variable and let them read it directly:
(def ^:dynamic *config* nil)
(defn some-function-using-config
[]
(println *config*))
(defn -main
[]
(binding [*config* (read-config)]
(some-function-using-config)))
Which of the two to choose is a matter of taste and situation. With direct passing you make it explicit that a function is receiving config, with the dynamic variable you avoid having to include config as an argument to every single function you write, most of whom will just pass it on.
Both of these solutions work well for unit tests, since you can just rebind the dynamic variable to whatever config you want to use for each test.
TheQuickBrownFox had the answer, the file is read both at run-time, and a compile-time.
Fine for me! That is actually really cool!
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"
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.
I'm writing a small debugging library and I would like to let users choose how to display data structures. I was imagining that users could require it in this kind of way:
(ns some-user-namespace
(:require
[clojure.pprint]
[my.library :with-args {print-fn clojure.pprint/pprint}]))
Is something like this possible, and if not, how can I solve this problem?
It's not possible to do it this way. If you really to offer this kind of setup, you could provide a configuration function to be called by the user after the import:
(ns some-namespace
(:require [my.library]))
(my.library/print-with! clojure.pprint/pprint)
Ending function name with ! is an idiomatic way of indicating that it's causing some side effects.
In your library it could look like:
(ns my.library)
(def config (atom {:printer println}))
(defn print-with! [new-printer]
(swap! config assoc :printer new-printer))
(defn my-lib-print [foo]
((:printer #config) foo))
EDIT: For a solution that does not require global, mutable state you can use dynamic bindings.
Lib:
(ns my.library)
(def ^:dynamic *printer* println)
(defn my-lib-print [foo]
(*printer* foo))
Usage:
(binding [my.library/*printer* clojure.pprint/pprint]
(my.library/my-lib-print {:hello "World"}))
These are the only two ways for some kind of external, contextual configuration I can think of. The only alternative is pure higher order function:
(defn my-lib-print [printer foo]
(printer foo))
I just started on my first clojure project using lein, the code here:
(ns fileops.core
(:use
[clojure.core :only (slurp)]
[clojure-csv.core :only (parse-csv)]
[fileops.core]))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(read-file "sample.csv"))
(defn read-file
"open and read the csv file"
[fname]
(with-open [file (clojure.java.io/reader fname)]
(parse-csv (slurp fname))))
I tried to run this using "lein run" but I keep getting this error:
Caused by: java.lang.RuntimeException: Unable to resolve symbol: read-file in this context
at clojure.lang.Util.runtimeException(Util.java:219)
at clojure.lang.Compiler.resolveIn(Compiler.java:6874)
at clojure.lang.Compiler.resolve(Compiler.java:6818)
at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6779)
at clojure.lang.Compiler.analyze(Compiler.java:6343)
... 52 more
What am I doing wrong?
You have used only slurp from clojure core, meaning that every other core function is now unavailable to you :) Try changing your ns to use :require instead of :use, as this is more idiomatic.
One thing to note is that order does matter in clojure, so if you don't declare a function at the top of your file, as in C and some other languages, the earlier functions will not be able to make reference to them. This is what was causing your error before and is why I like to define my -main function at the bottom. It's a matter of style.
Another thing is that your -main function is taking variable args right now and not using them. In Clojure it is idiomatic to use _ to refer to a parameter that doesn't get used. You could use & _ to avoid error messages, for when the user passes in unnecessary args, but I would just have the -main function parameterless from the start. This is because nothing needs to be provided to main when you run the program, and errors do make debugging easier. It is good to know what is getting used and where. The sample.csv file is already provided and is having read-file called on it, so the program should run if your read-file function is correct and the sample.csv file is in the proper location.
Regarding your -main function, it would be nice to put a little test in there to see if it executes properly when you run it, so I changed it to print out the contents of the csv file onto your console. This way of printing from a file is efficient and worth studying in its own right.
Finally, Make sure you include clojure-csv.core in your project.clj file.
core.clj:
(ns fileops.core
(:require
[clojure-csv.core :refer [parse-csv]]))
(defn read-file
"open and read the csv file"
[fname]
(with-open [file (clojure.java.io/reader fname)]
(parse-csv (slurp fname))))
(defn -main []
(println (clojure.string/join "\n" (read-file "resources/test.csv"))))
project.clj:
...
:dependencies [[org.clojure/clojure "1.5.1"]
[clojure-csv/clojure-csv "2.0.1"]
...]
:main fileops.core
You need to declare fileops.core as :main, as shown above. This tells Leiningen what function to execute when you enter lein run. Very important and tricky stuff.
So now make sure you are in the root of your project directory and at the terminal, run the following:
lein clean
lein deps
lein run
Good luck!
Further reading:
8th light blog on name-spaces
flying machine studios explanation of lein run
read-file should be before main in your source OR you should put before -main a declare cause like that:
(declare read-file)