Search code examples
clojure

Clojure: Lein run unable to resolve symbol


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?


Solution

  • 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