monojohnny
monojohnny

Reputation: 6181

Clojure exception when reading a text file

I'm just trying to read a standard linux '/etc/passwd' file : split into records. This appears to work (all the lines are echoed to the terminal) but throws an exception at the end ? (See below)

What's up with this program?

(use 'clojure.java.io)
(use 'clojure.string)

(defn process_file[infile] (
        (defstruct user :username
                        :password
                        :uid
                        :gid
                        :comment
                        :home_dir
                        :shell)

        (def record_separator #":")

        (with-open [rdr (reader infile)]
                (doseq [line (line-seq rdr)]
                        (def fields (split line record_separator) )
                        (def user_record (apply struct user fields) )
                        (println (user_record :username) )
                )
        )
        )
)

; main
(process_file "/etc/passwd")


[ after all the lines read have been output ]
    Exception in thread "main" java.lang.ClassCastException: clojure.lang.PersistentStructMap$Def cannot be cast to clojure.lang.IFn
        at clojure.lang.Var.fn(Var.java:392)
        at clojure.lang.Var.invoke(Var.java:419)
        at user$process_file.invoke(readfile.clj:15)
        at user$eval14.invoke(readfile.clj:26)
        at clojure.lang.Compiler.eval(Compiler.java:6514)
        at clojure.lang.Compiler.load(Compiler.java:6955)
        at clojure.lang.Compiler.loadFile(Compiler.java:6915)
        at clojure.main$load_script.invoke(main.clj:283)
        at clojure.main$script_opt.invoke(main.clj:343)
        at clojure.main$main.doInvoke(main.clj:427)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.lang.Var.invoke(Var.java:415)
        at clojure.lang.AFn.applyToHelper(AFn.java:161)
        at clojure.lang.Var.applyTo(Var.java:532)
        at clojure.main.main(main.java:37)


java version "1.7.0_21"
OpenJDK Runtime Environment (IcedTea 2.3.9) (7u21-2.3.9-1ubuntu1)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)


Clojure 1.4.0

Upvotes: 0

Views: 252

Answers (2)

Kyle Burton
Kyle Burton

Reputation: 27548

You have an extra set of parenthesis around the entire body of your function. Removing it looks like the following (though I don't recommend writing code this way, see below):

(defn process_file[infile]
  (defstruct user :username
             :password
             :uid
             :gid
             :comment
             :home_dir
             :shell)

  (def record_separator #":")

  (with-open [rdr (reader infile)]
    (doseq [line (line-seq rdr)]
      (def fields (split line record_separator))
      (def user_record (apply struct user fields))
      (println (user_record :username)))))

In your program, you are using def inside a function, which is discouraged. A more idiomatic way of writing your code is to use a namespace, and to define the struct and the record separator outside of your function, then use let within your function for local variables. Reformatting the code in that way looks like this:

(ns scratch
  (:require
   [clojure.java.io :as io]
   [clojure.string :as string]))

(defstruct user-rec
  :username
  :password
  :uid
  :gid
  :comment
  :home-dir
  :shell)

(def record-separator #":")

(defn process-file [fname]
  (with-open [rdr (io/reader fname)]
    (doseq [line (line-seq rdr)]
      (let [fields (string/split line record-separator)
            user   (apply struct user-rec fields)]
        (println (format "user: %s" user))))))


(process-file "/etc/passwd")

The above code runs, printing out the struct's created for each line of the password file.

Upvotes: 7

omiel
omiel

Reputation: 1593

The cause of your exception is the extra parenthesis before your defstruct call. A function is expected in leading position in a form, so the compiler tries to cast the result of defstruct to a function, hence the exception.

Upvotes: 4

Related Questions