1

Hi I have this school project that I am almost done with so I don't need help with the code, problem is I've never coded in clojure but for this assignment had to do a try and catch macro in clojure with bound forms, there are some REPL commands that are expected to give different responses for the assignment to pass,,

Anyways Im getting an error that I've been googling but nothing is specific to this problem and most explanations basically need there own explanation nothing seems to be beginner adapted so it doesnt do much for me.

(defmacro safe [bindings & code]
(if (list? bindings)
`(try 
   ~bindings 
  (catch Throwable except# except#)) 

(if (= (count bindings) 0)
  `(try ~code 
     (catch Throwable except# except#)) 

  `(let ~(subvec bindings 0 2)

     (try
       (safe ~(subvec bindings 2) ~@code)
       (catch Throwable except# except#) 

       (finally
         (. ~(bindings 0) close))))))) ;;safe



(def divider(safe (/ 1 0)))
(def reader (safe [s (FileReader. (java.io.File. "C:/text.txt"))] (. s read)))

So the error Im getting is

=> (def v (safe [s (FileReader. (java.io.File. "C:/text.txt"))] (. s read)))
#'myProject.core/v
=> v
#<ClassCastException java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn>

So kindly anyone that knows clojure please give me some hints on what is wrong, all the hints Im getting is that there should be a paranthesis missplaced, I've checked the code over and over but cant find any misstypes etc. Thank you !

3 Answers 3

5

Use macroexpand and friends to help debug

(def form (quote (safe [s (FileReader. (java.io.File. "test.txt"))] (. s read))))

(pprint (macroexpand form)) ;=>
(let*
 [s (FileReader. (java.io.File. "test.txt"))]
 (try
  (user/safe [] (. s read))  ; <--- Macro is recursive
  (catch java.lang.Throwable except__1104__auto__ except__1104__auto__)
  (finally (. s user/close))))

Macro is recursive, so this is not the full expansion

(pprint (clojure.walk/macroexpand-all form)) ;=>
(let*
 [s (new FileReader (new java.io.File "test.txt"))]
 (try
  (try
   ((. s read)) ; <-- Too many parenthesis! 
   (catch ...

The read call returns an integer, which is in turn being called as a function in the recursive macro expansion.

Sign up to request clarification or add additional context in comments.

6 Comments

I'm not on the computer right now, but that looks pretty good! So macroexpand is debugger? And the code you've written is written in the REPL right?
It is useful in debugging, but it is not a debugger. It does what it says - shows the expansion of the macro before it is evaluated. You may find it instructive make a function version, safe-fn, of the macro with defn instead of defmacro and then note that calling safe-fn with all its arguments individually quoted gives the same output as doing a macroexpand-1 on the quoted macro call. Yes, that's from a REPL session.
So let me get this straight, is my error beeing caused by an extra paranthesis? So all I have to do is find it and get rid off it and the error would go away?
Mate' I've tried relentlessly all morning to get the same results you are getting, I'm not sure how to use the friend part of your comment, but proceeded with testing the macroexpand please tell me if Im doing it right: So this is saving away form so that it can be pprinted, right? user=> (def form (quote (safe [s (FileReader. (java.io.File. "test.txt"))] (. s read)))) Here we're expanding the form so we can debug/trail what is happening before it is evaluted, right? user=> (clojure.print/pprint (macroexpand form)) Next the expansion should be printed? Im I missing out on some part?
You can see from one level of macroexpand that there are no visible extra parenthesis. With the recursive expansion there is. Therefore, perhaps you didn't intend for safe to be called recursively or you need to do some unquote-splicing. About the REPL - correct. You'll need to make the definition of safe available to your REPL namespace (e.g. a use or require directive or copy/paste your code).
|
0
 (FileReader. (java.io.File. "/tmp/text.txt"))

is giving you the value of the first character on the file

cat /tmp/text.txt => abcd

(let [s (FileReader. (java.io.File. "/tmp/text.txt"))] (. s read)) => 97

same as

(int \a)

and your macro expect a function. try:

(safe [s (FileReader. (java.io.File. "/tmp/text.txt"))] #(+ 6)) or (safe [s (FileReader. (java.io.File. "/tmp/text.txt"))] (fn [] (. s read)

2 Comments

Hi thanks for the help but the aasignment says specifically that the commands I mentioned above should look that way if I would change it, it would probably be looked as Im taking shortcuts
I must say this is actually working, ! Still unsure though if Im allowed to use it this way.
0

Solved!

Missing unquote slicing in the third try block, causing exception

java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn

this was caused due to extra paranthesis when reading s recursivly,

macroexpand ex.

(pprint (macroexpand-all form)) 
 (let*[s
  (new FileReader 
   (new File "test.txt"))]
    (try
     (try
       ((.s read)) ;<= here

Had to change the third try/catch from

(try
   (safe ~(subvec bindings 2) ~@code)

to:

(try
   (safe ~@(subvec bindings 2) ~@code)

To my understanding the problem was that I was returned a list with a paranthesis like (1(2 3)) causing exception when read, unquoting makes it look (1 2 3) and can be read or (in this case) casted from java to clojure.

reservations for misspellz or calling shit the wrong name! :)

Many thanks to A.Webb and patz.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.