Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Tools by

Trying to run my first Clojure program while following this guide and I'm running into problems.

Here's my basic code:

(ns clojure-noob.core
  (:gen-class))

(defn -main
  "I don't do a whole lot...yet."
  [& args]
(println "Hello, World!"))

(defn train
  []
  (println "Choo choo!")) 

And here's what I get after switching namespaces:

clojure-noob.core> (train)

Syntax error compiling at (*cider-repl Clojure/Projects:localhost:49632(clj)*:46:20).
Unable to resolve symbol: train in this context
clojure-noob.core> (-main)
Syntax error compiling at (*cider-repl Clojure/Projects:localhost:49632(clj)*:49:20).
Unable to resolve symbol: -main in this context
clojure-noob.core> 

What step am I missing?

And why is the learning curve so steep?

3 Answers

+1 vote
by

What is most likely is that you're switching to the namespace but your code isn't being evaluated first. You are in the correct namespace but it is empty.

So if you are in a shell and open up a REPL with something like:

$ pwd
/home/whoever/programming-clojure
$ tree
.
└── clojure_noob
    └── core.clj

1 directory, 1 file

$ clj
Clojure 1.10.3
user=> (ns clojure-noob.core)
nil

clojure-noob.core=> (train)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: train in this context

clojure-noob.core=> (require 'clojure-noob.core) ;; try to load code'
Execution error (FileNotFoundException) at user/eval4 (REPL:1).
Could not locate clojure_noob/core__init.class, clojure_noob/core.clj or clojure_noob/core.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.

clojure-noob.core=> (System/exit 0) ;; That did not work
$ mkdir src
$ mv clojure_noob/ src/
$ clj
user=> (ns clojure-noob.core)
nil
clojure-noob.core=> (require 'clojure-noob.core) ;;' ask.clojure syntax
nil
clojure-noob.core=> (train)
Choo choo!
nil

What is happening there?

  1. Clojure is initialised. The clojure.core namespace is loaded in, user namespace loaded in and the core functions referred.

  2. Switch to the clojure-noob.core namespace. clojure.core namespace is referred by default (and some other stuff - https://clojure.org/reference/namespaces). Clojure hasn't even attempted to read clojure_noob/core.clj yet though.

  3. Try to read functions into the clojure-noob.core namespace with require. We hit a classic Clojure footgun - it can't find the file.

  4. We use forbidden knowledge that Clojure adds src/ to the classpath but not $PWD. In theory you can work this out from the documentation but don't ask me where, I've never managed to understand how people are meant to figure that out. Rearrange the directory structure.

  5. Now it works.

My unsolicited advice: when something goes wrong with dependency management, head straight to ask.clojure.org - it is a waste of time reading the docs or trying to work it out yourself. This stuff is really hard to work out, it took me months to get comfortable. No language is perfect. Clojure has rewards for toughing it out though, once the namespaces are set up it is a lot of fun.

by
Thanks -- I tried this out but I'm still getting the same error at the end.

Let me retry it after matching your directory structure.
by
I tried again, and got the same result:

clojure-noob.core=> (train)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: train in this context
by
Weird. There isn't enough information to diagnose exactly what is going wrong here, but some things you can try:

1] Run (map println (all-ns)) to get a list of what namespaces are loaded. Maybe there is a typo or something and functions are being loaded into the wrong namespace.

2] Try (require 'clojure-noob.core) to check that Clojure can find the relevant file.

3] (ns-publics 'clojure-noob.core) to see what, if any, symbols are defined in the namespace.

That error is one of Clojure's simpler ones - you are in the right namespace, but (train) isn't there. It must be either the file is not being found (which can be checked with the require call) or there is something like a typo and the function is getting lost during the import.

If the file is being found, you're going to need to do some digging with exploratory functions for anyone to help you. Like the ones in https://clojure.org/reference/namespaces
+1 vote
by

What owenRiddy said.

I think that the step you are missing is right below when the guide has you writing the (train) function, it tells you to recompile the file:

When you’re done, save your file and use C-c C-k to compile your current file within the REPL session. (You have to compile your code for the REPL to be aware of your changes.) Now if you run (train) in the REPL, it will echo back Choo choo!.

It is the equivalent to when owenRiddy does

clojure-noob.core=> (require 'clojure-noob.core)

See Navigating namespaces in the official Clojure guide for some excellent pointers.

0 votes
by
edited by

Using the clj command line, if I just copy/paste what you have into the repl:

PS C:\Users\joinr> clj
Clojure 1.10.2
user=> (ns clojure-noob.core
         (:gen-class))

(defn -main
  "I don't do a whole lot...yet."
  [& args]
  (println "Hello, World!"))

(defn train
  []
  nil

#'clojure-noob.core/-main
#'clojure-noob.core/train

and then I evaluate (train) and (main):

clojure-noob.core=> (train)
Choo choo!
nil

clojure-noob.core=> (-main)
Hello, World!
nil

Everything seems fine. What are you doing differently?

[ed] I noticed you are following CFTBT. Is this your first time with emacs? If so, a simpler editor/setup may be better, so that you are not learning both clojure and emacs at the same time....(I say this as an emacs user, specifically spacemacs with the default clojure layer and minimal customization....).

...