including multiple files in a Clojure file - clojure

I am very new to Clojure. I know there is a way to include files in other files using a keyword like #include for C++. and i have done some digging and found a few things pointing to the keyword declare. Maybe I'm not using it correctly but it doesn't seem to work for me.
I am trying to write a function, for example, get_all_preds that uses another function predecessors, written in another file. the files are both under the directory src and each function is the only code in their respective files.
When i add (declare bar.clj) to the top of the foo.clj file i still get an error that bar cannot be found.
(defn get_all_preds
([tasks job]
(cond (empty? (predecessors tasks job)) (println "No predecessors")
:else (get_all_preds tasks (predecessors tasks job) (empty tasks))))
([tasks prereqs job]
(cond (empty? prereqs) job
:else (get_all_preds tasks (distinct (flatten (concat (predecessors tasks (first prereqs)) (rest prereqs))))
(distinct (cons (first prereqs) job))))))
(defn predecessors
[tasks-list job]
(cond (empty? tasks-list) 0
(= job (first (first tasks-list))) (rest (rest (first tasks-list)))
:else (predecessors (rest tasks-list) job)))

declare is for creating a var without binding it to a value.
The proper function for loading another Clojure namespace is require. To use require, you need to use namespaces, and create files with paths reflecting the semantic structure of the namespaces the files hold. The closest equivalent to #include is load, but this should be reserved for unstructured experiments where you might as well be copy / pasting into the repl, or unusual situations where namespaces are not appropriate.
A quick example:
in file src/foo/bar.clj:
(ns foo.bar)
(defn frob [] "hello")
in file src/foo/baz.clj:
(ns foo.baz
(:require [foo.bar :as bar]))
(defn frob [] (str (bar/frob) (bar/frob)))
(defn -main [& args]
(println (frob))
Running foo.baz/-main will print "hellohello" followed by a new line.
A Clojure namespace is a more structured concept than the compilation units of C, and is something that you can directly interact with in your code in a granular way. It will seem strange at first, but it allows fine grained control of what definitions are visible from various parts of your code (which in turn helps make code much easier to maintain and understand).

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.

Can reader tags be used with ClojureScript

In Clojure, adding custom reader tags is really simple
;; data_readers.clj (on classpath, eg. src/clj/)
{rd/qux datareaders.reader/my-reader}
;; Define a namespace containing the my-reader var:
(ns datareaders.reader)
(defn my-reader [x] 'y)
;; use special tag in other namespace. Readers have to be required first.
(require 'datareaders.reader)
(defn foo [x y]
(println #rd/qux x "prints y, not x due to reader tag."))
I am trying to achieve the same thing for ClojureScript but am getting an error that #rd/qux is not defined. I am using lein cljsbuild once to build the project. Is that a limitation of ClojureScript or is it that cljsbuild builds the project before the readers have been resolved? In that case, how can I force leiningen to load the readers namespace before cljsbuild is started?
EDIT: Note that this example intends to use reader tags within ClojureScript source code and not when reading auxilliary data via read-string.
This currently isn't possible, but will be as soon as #CLJS-1194 and #CLJS-1277 are fixed. Hopefully that will happen very soon.
If you wanted to do it, just rename data_readers.clj to data_readers.cljc and use conditional readers.
As an aside, what's your use case for this?
Both #CLJS-1194 and #CLJS-1277 are fixed, so this should work as expected.
The mechanism to add a custom reader tag in cljs is different. You have to call register-tag-parser! which takes a tag and a fn.
From the cljs reader tests:
(testing "Testing tag parsers"
(reader/register-tag-parser! 'foo identity)
(is (= [1 2] (reader/read-string "#foo [1 2]")))
Your example would be:
(cljs.reader/register-tag-parser! 'rd/qux (fn [x] 'y))
(defn foo [x y]
(println #rd/qux x "prints y, not x due to reader tag."))

Aspect-Oriented Programming in Clojure

How to achieve Aspect-Oriented Programming in Clojure? Do we need AOP in Clojure?
Let's say we want plain vanilla Clojure solution (no AspectJ).
Aspect-Oriented Programming is typically used to add cross-cutting functionality to code that would otherwise get hopelessly intertwined with business logic. A great example is logging - you don't really want logging code scattered everywhere in your code base.
You don't really need AOP in Clojure because it's easy to achieve this with other techniques in Clojure.
For example, you can use higher-order functions to "wrap" other functions with cross cutting functionality:
; a simple function - the "business logic"
(defn my-calculation [a b]
(+ a b))
; higher order function that adds logging to any other function
(defn wrap-with-logging [func]
(fn [& args]
(let [result (apply func args)]
(println "Log result: " result)
result)))
; create a wrapped version of the original function with logging added
(def my-logged-calculation (wrap-with-logging my-calculation))
(my-logged-calculation 7 9)
=> Log result: 16
=> 16
AOP IMHO is just an artifact of certain kinds of static programming languages. AFAIKS it's usually just a bunch of non-standard compiler extensions. I've not yet seen any application of AOP that can't be solved better & natively in more dynamic languages. Clojure is certainly dynamic enough, and that's without even considering macros.
I may be wrong, but if so, I'd need to see an actual AOP use case that can't be implemented just as well in pure clojure.
Edit: just to be clear: I refuse to see things like elisp's advice as aspect oriented. In dynamic languages, those are just techniques to be used whenever you need them, with no need for language support other than rebinding of function definitions - which all lisps support anyway.
There's no need to treat them as special - you can easily define your own defadvice-like function in clojure. See for example, compojure's wrap! macro, which is actually deprecated since you generally don't even need it.
Aspect oriented programming is a great way to achieve seperation of concernes in Java. Clojure's composable abstractions achieve this very well. See this question also. The topic is covered really well in The Joy Of Clojure.
as for an example of Aspect Oriented Clojure by another name check out the Ring web framework
Well you could be more AOP w/ Clojure easily. Just use metadata in functions to informe when you want logs:
(defn ^:log my-calculation
[a b]
(+ a b))
Then you can redefine all functions, wrapping them w/ logging automatically. Part of this code (together w/ unwrap functions bellow):
(defn logfn
[f topic severity error-severity]
(fn [& args]
(try
(if severity
(let [r (apply f args)]
(log* topic {:args args, :ret r} severity)
r)
(apply f args))
(catch Exception e
(if error-severity
(let [data {:args args, :error (treat-error e), :severity error-severity}]
(log* topic data error-severity)
(throw e))
(throw e))))))
(defn logfn-ns
"Wrap function calls for logging on call or on error.
By default, do nothing. When any :log or :log-error, enables logging. If ^:log,
only log on error (default severity error).
Can customize log severity w/ e.g. ^{:log info} or on error log severity likewise."
[ns alias]
(doseq [s (keys (ns-interns ns))
:let [v (ns-resolve ns s)
f #v
log (-> v meta :log)
log-error (-> v meta :log-error)]
:when (and (ifn? f)
(-> v meta :macro not)
(-> v meta :logged not) ;; make it idempotent
(or log log-error))]
(let [log (if (= log true) nil log)
log-error (or log-error "error")
f-with-log (logfn f
(str alias "/" s)
log
log-error)]
(alter-meta! (intern ns s f-with-log)
(fn [x]
(-> x
(assoc :logged true)
(assoc :unlogged #v)))))))
(defn unlogfn-ns
"Reverts logfn-ns."
[ns]
(doseq [s (keys (ns-interns ns))
:let [v (ns-resolve ns s)]
:when (-> v meta :logged)]
(let [f-without-log (-> v meta :unlogged)]
(alter-meta! (intern ns s f-without-log)
(fn [x]
(-> x
(dissoc :logged)
(dissoc :unlogged)))))))
You just call (log/logfn-ns 'my.namespace "some alias") and all is wrapped w/ logging (and some).
PS: My custom logger above have a topic which is "some alias/function name"
PS2: Also wrapped w/ try/catch.
PS3: Didn't like this so much. Reverted to have explicit logging.

Setting Clojure "constants" at runtime

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* ...))

Finding vars from dynamically created namespaces in clojure

The following test fails:
(ns clojure_refactoring.rename-fn-test
(:use clojure.test))
(deftest test-fn-location
(in-ns 'refactoring-test-fn-rename)
(clojure.core/refer-clojure)
(defn a [b] (inc b))
(in-ns 'clojure_refactoring.rename-fn-test)
(is (not= (find-var 'refactoring-test-fn-rename/a)
nil))
(remove-ns 'refactoring-test-fn-rename))
That is, find-var (of a var I've just created, in a namespace I've just create) returns nil. This behaviour doesn't happen at the repl, where typing out the steps of the test works just fine.
Am I doing something wrong, or is this just something that doesn't work in clojure right now?
Updated to a version which really seems to do the intended thing, in contrast to my original answer...
This version seems to work:
(ns clojure-refactoring.rename-fn-test
(:use clojure.test
[clojure.contrib.with-ns :only [with-ns]]))
(deftest test-fn-location
(create-ns 'refactoring-test-fn-rename)
(with-ns 'refactoring-test-fn-rename
(clojure.core/refer-clojure)
(defn a [b] (inc b)))
(is (not= (find-var 'refactoring-test-fn-rename/a)
nil))
(remove-ns 'refactoring-test-fn-rename))
Also, you really need to change all occurrences of _ in namespace names to -, and the other way around for filenames.
With these changes in place, the test runs fine for me. (I haven't even tried to run it Apparently it still works without making the _ / - changes, but really, you need to do it! That's the accepted convention and things are not guaranteed to work if you don't follow it.)
For some reason, the code from the question seems to have been creating the Var a in the namespace the test was being defined in, thus (find-var 'clojure-refactoring.rename-fn-test/a) was returning a Var, whereas the test failed. With the above, (find-var 'clojure-refactoring.rename-fn-test/a) returns nil, as expected.