Search code examples
clojurenullpointerexceptionatomic

NullPointerException on iterating over a file on atom


I'm new to Clojure, and I'm building a small function that reads tuples from a file and check if the first element is already in a (atom{}).

But I keep receiving NullPointerException at line ((println "OK"))) after the first iteration. What am I doing wrong? Here is the code:

(defn graph-from-file
  "Expects a string with the path for a file with a list of edges, one in each line,
And builds a graph data structure from these"
  [filepath]
  (def gr (atom{}))
  (with-open [rdr (reader filepath)]
    (doseq [line (line-seq rdr)]
      (let [[src dst] (str/split line #" ")
            ks (keyword src)]           ;define ks as the keyword
        (println (str "src: " src " dst: " dst " kw: " ks))
        (if (contains? @gr ks)
          ((println "WHAT?"))
          ((println "OK")))
        )))
  )

Note that the code is simple, and the outputs (WHAT? and "OK") are here just for demonstration purposes.

Here is the output I get:

src: 64 dst: 48 kw: :64
OK
NullPointerException   ****/graph-from-file (core.clj:19)

Solution

  • You have an extra layer of parentheses: ((println "OK")).

    The result of (println "OK") is a nil, so the extra pair of parenthesis looks like a function call on nil: (nil). In Java the equivlant code would be null(), which doesn't make any sense.

    Remember that in Clojure, parentheses mean "function call".


    UPDATE 2015-9-17:

    If function syntax is:

    (if <cond-expr>
      <true-expr>
      <false-expr> )
    

    The 3 expressions may be either constant values like 5 or a function call like (+ 2 3). The return value of the entire (if ...) expression is the result of either <true-expr> or <false-expr>. So we get:

    (if true
      :wahoo
      "no such luck" ))
    ;=> :wahoo
    

    and

    (if (< (+ 2 3) 9)
      (str "Two plus three is " (+ 2 3))
      :not-likely ))
    ;=> Two plus three is 5
    

    UPDATE 2015-11-1

    The syntax is as follows:

    (if test-value
      result-value-if-true
      result-value-if-false)
    

    The first example results in :wahoo with all three values being literals (i.e. constants). In Clojure, any value can be replaced by an expression:

    (if (< 2 3)
      (+ 9 10)
      (- 9 10))
    ;=> 19
    

    Each value in the above example has been replaced by a function call. The result of (< 2 3) is true, so the function (+ 9 10) is evaluated and the result of that function is returned as the result of the whole (if ...) expression. So, we get the result 19 instead of -1.

    Remember, in Clojure parentheses mean "function call". You have to un-learn the idea from Java, et al that parentheses are often used as a "grouping operator". In Java,

    2 = (2) = ((2)) = ...
    

    since repeatedly "grouping" a value makes no difference. In Clojure, the syntax (2) means "find the function named 2 and call it with zero arguments (in Java this would be 2() which is illegal).