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

0 votes
in Compiler by

Hi!
I have 2 files in my clojure project.
The first one (db.clj) reads into a txt file and store the data into a list of vectors and the second one (menu.clj) shows a menu with many options.
My conditions work : tables are displayed as wanted, however I get an awkward error at the end and my app stops running.
Details :
db.clj :

(ns db)    
(defn load-data
  [filename]
  (with-open [rdr (io/reader filename)]
    ;; https://clojuredocs.org/clojure.core/doall
    (doall
      (for [line (line-seq rdr)
            :let [[id & data] (clojure.string/split line #"\|")]]
        [(Integer/parseInt id)
         data]))))

menu.clj :

(ns menu
  (:require [db])
  )


(def customers (db/load-data "cust.txt"))
(def products (db/load-data "prod.txt"))

(def input_ 0) ; initialize input   

(defn menu
  []
  (if (not= input_ "6")
    (do
      (newline) ; insert a new line
      (println " ** MENU ** ") ; display menu
      (println "
1. Display Customer Table
2. Display Product Table
3. Display Sales Table
4. Total Sales for Customer
5. Total Count for Product
6. Exit")
      (println "Enter an option > ")
      (flush)
      (def input_ (read-line)) ; put the input in input_ variable
      (cond
        (= input_ "1") 
          ((println "Table of customers: ")
          ; run >> every item in a newline
          (run! println (db/load-data "cust.txt")))
        (= input_ "2") 
          ((println "Table of products: ")
          ; run >> every item in a newline
          (run! println (db/load-data "prod.txt")))
        :else (println "Option not handled!")
        )
      ;(println (str "The option entered is: \"" input_ "\""))
      (menu))
    (println "Goodbye!") ; If user prompts 6
    ))

Console :

    ** MENU ** 

1. Display Customer Table
2. Display Product Table
3. Display Sales Table
4. Total Sales for Customer
5. Total Count for Product
6. Exit
Enter an option > 
2
Table of products: 
[1 (shoes 14.96)]
[2 (milk 1.98)]
[3 (jam 2.99)]
[4 (gum 1.25)]
[5 (eggs 2.98)]
[6 (jacket 42.99)]
Syntax error (NullPointerException) compiling at (menu.clj:42:1).
null

Full report at:
/tmp/clojure-4521524603536338044.edn

Thanks!

1 Answer

+2 votes
by
selected by
 
Best answer
  1. You should have inspected & included the stack traces in /tmp/clojure-4521524603536338044.edn (the file path is in the error message).

  2. The problem might be in the cond statement:

(cond
     (= input_ "1") 
     ((println "Table of customers: ")
     (run! println (load-data "cust.txt")))) 

If input_ is "1", then the clause ((println "Table of customers: ") (run! println (load-data "cust.txt"))) runs. If we expand the parenthesis slowly, that becomes:

(nil nil)

(println evaluates to nil), which Clojure interprets as a function call, executing nil with 1 argument. Since nil isn't a function, it throws an exception. I ran that in Clojure 1.11.1 though and got a slightly different error message though.

The fix is to have a `(do ...) loop around the printing in the cond.

  1. You look like you are learning Clojure. As a point of advice, the language is designed to use (let) instead of (def input_ (readline)) and recur instead of the recursive call to menu. Well done on finding cond that is a good function here.
by
Thanks! However, (do...) didn't fix the problem. I still have the same Null Pointer error...
by
You probably typed it in wrong. This works for me:

    (ns menu
      (:require [db])
      )
    
    (def customers (db/load-data "cust.txt"))
    (def products (db/load-data "prod.txt"))
    
    (defn menu
      []
      (newline) ; insert a new line
      (println " ** MENU ** ") ; display menu
      (println "
    1. Display Customer Table
    2. Display Product Table
    3. Display Sales Table
    4. Total Sales for Customer
    5. Total Count for Product
    6. Exit")
      (println "Enter an option > ")
      (flush)
      (let [input_ (read-line)] ; put the input in input_ variable
        (cond
          (= input_ "1")
          (do (println "Table of customers: ")
              ;; run >> every item in a newline
              (run! println (db/load-data "cust.txt"))
              (recur))
    
          (= input_ "2")
          (do (println "Table of products: ")
                                            ; run >> every item in a newline
              (run! println (db/load-data "prod.txt"))
              (recur))
    
          (= input_ "6")
          (println "Goodbye!") ; If user prompts 6
    
          :else
          (println "Option not handled!"))))

You also need to include `(:require [clojure.java.io :as io])` when declaring the namespace for `db`.
...