Ertuğrul Çetin
Ertuğrul Çetin

Reputation: 5231

How to load Clojure files from directory to REPL while preserving dependency order?

I have 2 external Clojure files in my file system.(a.clj and b.clj)

/Users/e/.somedir/a.clj:

(ns a (:require [b]))

(b/printt)

/Users/e/.somedir/b.clj:

(ns b)

(defn printt [] (println "aaaa"))

The thing is I want to be able to load those files to my REPL(preserving dependency order)

If I load a.clj first I get this exception:

(clojure.main/load-script "/Users/e/.somedir/a.clj"):

CompilerException java.io.FileNotFoundException: Could not locate b__init.class or b.clj on classpath., compiling:(/Users/e/.somedir/a.clj:1:1)

If I load b.clj first there is no problem.

So how can I figure this dependency order out and load b.clj first, imagine that this is a complex dependency graph, are there any code examples?

P.S: I don't want to use leiningen or any other build tool(want to do it programmatically). There could be some kinda library/code snippet that shows me files that needed to get loaded in dependency order. How do build tools like leiningen achieve this thing? They do it in some way.

Upvotes: 0

Views: 2292

Answers (4)

Ivan Grishaev
Ivan Grishaev

Reputation: 1679

You say do not want to use lein or any other build tool, and that's fine. So you'd be better just to organize your code according to Deps & CLI Guide about the latest CLI tools released with Clojure 1.9.

Briefly, in your folder, create another src folder and put all the .clj files there. The system will find them automatically. For example:

└── your-project
    ├── deps.edn   # your dependencies
    └── src
        └── a.clj
        └── b.clj

Then, in the root of your project, just run clj, and the REPL will appear. There, you may require your modules as well.

Take a look at this article, it would really help.

Upvotes: 1

Ertuğrul Çetin
Ertuğrul Çetin

Reputation: 5231

I think I found the solution first of all I created dependency graph then I sorted namespaces in topological order so this is the thing I've been looking for a while. Now I can load Clojure files in that order(increasing dependency order).

Upvotes: 0

Jon Seltzer
Jon Seltzer

Reputation: 83

You don't need to load all the files, just the root file of your dependency tree. The other source files are loaded as needed. So if namespace A requires namespace B and namespace B requires namespace C, then you just:

(load "com/example/A.clj")

and B.clj and C.clj are loaded automatically.

Also, since you're not using a build tool, you'll need to be careful about a few things:

1) You need to make sure your source files folder structure mirrors your namespace declarations so an ns of 'com.example.A' should be in a folder structure like src/com/example/A.clj. If you don't Clojure has no way to know where a dependency is located in the file system.

2) A running Clojure REPL is initiated with a specific class path. If the 'src' folder above was not defined on the classpath, Clojure doesn't know about it. So be careful to add that folder to your classpath: java -jar clojure-1.9.0.jar -cp [need to include your src folder here] clojure.main

One last note, running without a build tool like Leiningen means you'll need to be an expert on java class paths and understanding Clojure's dependency model. That's a lot for someone trying to learn Clojure.

Upvotes: 2

Alan Thompson
Alan Thompson

Reputation: 29984

This is easy if you set up your project using lein. Here is an example project fred. To create a simple project, just type:

> lein new app fred
> cd fred
> cp src/fred/core.clj  src/fred/second.clj

It now looks like so:

fred
└── src
    ├── fred
        ├── core.clj
        └── second.clj

with 2 source files core.clj and second.clj. Add your functions:

> cat src/fred/core.clj 

(ns fred.core)

(defn aaa [& args]
  (println "aaa - enter")
  (println "aaa - exit"))


> cat src/fred/second.clj 

(ns fred.second
  (:require [fred.core :as fc]))

(defn bbb [& args]
  (println "bbb - enter")
  (fc/aaa)
  (println "bbb - exit"))

Then, you can access both using lein repl:

 > lein repl
Clojure 1.9.0

fred.core=> (aaa)
aaa - enter
aaa - exit
nil
fred.core=> (require '[fred.second :as fs])
nil
fred.core=> (fs/bbb)
bbb - enter
aaa - enter
aaa - exit
bbb - exit
nil
fred.core=> 

You can also use the following variant to reload a namespace and its dependencies:

(require '[fred.second :as fs] :reload-all)

although this is not perfect and can get into a confused state (see How to reload a clojure file in REPL for more details).

Upvotes: 0

Related Questions