I just started playing with Clojure, and I wrote a small script to help me understand some of the functions. It begins like this:
(def *exprs-to-test* [
"(filter #(< % 3) '(1 2 3 4 3 2 1))"
"(remove #(< % 3) '(1 2 3 4 3 2 1))"
"(distinct '(1 2 3 4 3 2 1))"
])
Then it goes through *exprs-to-test*
, evaluates them all, and prints the output like this:
(doseq [exstr *exprs-to-test*]
(do
(println "===" (first (read-string exstr)) "=========================")
(println "Code: " exstr)
(println "Eval: " (eval (read-string exstr)))
)
)
The above code is all working fine. However, (read-string exstr)
is repeated so I tried to use let
to eliminate the repetition like so:
(doseq [exstr *exprs-to-test*]
(let [ex (read-string exstr)] (
(do
(println "===" (first ex) "=========================")
(println "Code: " exstr)
(println "Eval: " (eval ex))
)
))
)
But this works once for the first item in *exprs-to-test*
, then crashes with a NullPointerException
. Why is the addition of let
causing the crash?
You have an extra set of parentheses around the do
form. Your code is doing this:
((do ...))
It's trying to execute (as a function call) the value of the entire do
form, but do
is returning nil
, because the last println
in the do
form returns nil
.
Note, your indentation style is non-standard. You shouldn't put the closing parens on their own lines. And let
has an implicit do
so you don't need one there. Try this:
user> (doseq [exstr *exprs-to-test*]
(let [ex (read-string exstr)]
(println "===" (first ex) "=========================")
(println "Code: " exstr)
(println "Eval: " (eval ex))))
=== filter =========================
Code: (filter #(< % 3) '(1 2 3 4 3 2 1))
Eval: (1 2 2 1)
=== remove =========================
Code: (remove #(< % 3) '(1 2 3 4 3 2 1))
Eval: (3 4 3)
=== distinct =========================
Code: (distinct '(1 2 3 4 3 2 1))
Eval: (1 2 3 4)
I think the other answers are ignoring the elephant in the room: why are you doing this? There are a lot of things in your code that make me worry you are forging on the wrong path through learning Clojure:
The best way to learn the APIs of Clojure is through the REPL. You should get your environment set up, be it Vim, Emacs, or an IDE such that you can easily move back and forth between static code in text files and an interactive REPL. Here is a good breakdown of a number of Clojure IDEs.
Now, as far as your code goes, a few things to remember. First, there's almost never a good reason to use eval. If you find yourself doing this, ask yourself if its really necessary. Second, remember, Clojure is a functional language and generally you should not need to use the "do" set of macros. The "do" macros are useful when you need to have side effects (in your example, the side effect is the println to *out*) Finally, using global vars should be avoided as well. If you do need to use vars, you should consider using the bindings macro to bind the vars locally to the thread to immutable values so there are no concurrency issues.
I definitely recommend you take the time to pick up Programming Clojure or another deeper reference to LISP to truly understand the shift necessary in the way you think about programming to leverage Clojure effectively. Your small sample here makes me feel as though you are trying to write imperitive code in Clojure, which is not going to work well at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With