How to catch an arity exception in Clojure? - clojure

I am trying to catch an arity exception like so:
(try
(inc)
(catch clojure.lang.ArityException e
(str "caught exception: " (.getMessage e))))))
In this case, I am invoking inc without a passing it a number, and it rightly throws an exception. However, that exception does not get caught when I run it:
(try
(inc)
(catch clojure.lang.ArityException e
(str "caught exception: " (.getMessage e))))))
; => CompilerException clojure.lang.ArityException: Wrong number of args (0) passed to: core/inc--inliner--4489
Attempting to catch any Exception generally instead of clojure.lang.ArityException still throws it.
I am sure anyone seasoned in Clojure development will catch my mistake immediately.

You can catch ArityExceptions in some cases; depending on the context and the function causing the ArityException.
I'm going to admit right off the bat that I'm speculating on some aspects because I've never explored this in too much depth before.
First, look what happens if you call inc in a context where it's obvious that the number of supplied arguments is wrong:
(inc)
CompilerException clojure.lang.ArityException: Wrong number of args (0) passed to: core/inc--inliner--4489, compiling: . . .
The interesting part is inc--inliner--4489. If you look at the definition for inc, it has this meta-data attached to it:
:inline (fn [x] `(. clojure.lang.Numbers (~(if *unchecked-math* 'unchecked_inc 'inc) ~x)))
I've never looked into :inline in too much depth before, but I've always been under the assumption it means that it will attempt to inline the call to (marginally) reduce overhead. In this case, it's attempting to inline inc to just be a call to either clojure.lang.Numbers/inc, or clojure.lang.Numbers/unchecked_inc; depending on the state of *unchecked-math*. Also note how the error starts:
CompilerException clojure.lang.ArityException
In your example, you can't catch straight (inc) because that call fails at compile time before the code is even run. It knows that (inc) will never be correct, so it fails immediately. This is a good thing though. (inc) will never be correct, so there's no point in trying to catch it anyways.
There are circumstances however where catching an arity exception may make sense (although there's likely better ways of approaching the problem*). Say you disallow the call to inc to be inlined by, as #Rulle suggested, applying the arguments:
(try
(apply inc [])
(catch clojure.lang.ArityException e
(println "Caught!")))
Caught!
The compiler can't know for sure whether or not inc will fail because it depends on the number of arguments supplied to apply; which may depend on things at runtime. In this case, the code is actually capable of being run, and is also capable of having its ArityException caught.
Taking :inline out of the equation, you can also see that a custom function of yours can have its ArityException caught with less fuss, since the call isn't inlined, so it doesn't fail at compile time:
(defn hello [arg])
(try
(hello) ; No apply
(catch clojure.lang.ArityException e
(println "Caught!")))
Caught!
* I can't say I've ever had to catch an ArityException, and I can't think of any circumstances where doing so would be appropriate. Even if you're using apply, it would make more sense to verify the arguments ahead of time, or even rethink your use of apply. If using apply leads to an ArityException, you probably have a flaw in your logic somewhere that use of try is just putting a band-aid over.

Related

Negative arity exception in recursive macro

I'm trying a coding challenge that requires you to create code that compiles infinitely.
My first thought was a macro that expands to itself forever. I wrote up:
(defmacro a []
(a))
(a)
This doesn't actually produce anything, but I expected it to loop forever. Instead however, I get a nonsensical arity exception:
Wrong number of args (-2) passed to: infinite-compile/a, compiling:...
If I try to give it an argument for kicks, it now complains that the macro doesn't expect any arguments.
If I have it actually produce calls to itself:
(defmacro a []
`(a))
(a)
It fails with a StackOverflow, which I expected.
What's going on here? Why does it think I'm passing the macro "-2" arguments? The only possible thing I could think of is it has something to do with the 2 implicit & arguments that are passed to macros, but that's just a shot in the dark and doesn't actually explain what's going on.
To address the answer, this doesn't appear to be an issue with a multi-arity macro since it only has a 0-arity version. Also, explicitly passing the implicit arguments doesn't do anything:
(defmacro a []
(a &form &env))
This yields:
Compiler Exception clojure.lang.ArityException: Wrong number of args (2) passed to: infinite-compile/a, compiling:
You are expecting (a) to be macroexpanded during the definition of a, when a is not yet known to be a macro (and thus expected to be a function), whereas when you quote the form, you are effectively building a macro which expands into a call to itself.
When the compiler macroexpands a macro, it adds the implicit arguments before calling the function associated with the macro: Compiler.java#L6795. Here, you are directly calling a, which is in the scope of the implicit defn (core.clj#L452), without passing the necessary arguments.
I would expect the following to work as you wish (loop):
user=> (defmacro a[]&form)
#'user/a
user=> (a)
But unfortunately, here is the error message I get:
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'user/a, compiling:(/tmp/form-init5239882861501900074.clj:1:1)
... even though:
user=> (defmacro a[](print &form))
#'user/a
user=> (a)
(a)nil
NB. The Common Lisp equivalent is:
(defmacro w(&whole w)w) ;; 23 bytes
Existing names
Note also that once you define a, your cannot change the definition as follows:
(defmacro a[](a 0 1))
... because it complains that a accepts zero arguments. If, however, you define another macro with a name which has not yet been defined, it works:
user=> (defmacro b[](b 0 1))
#'user/b
user=> (b)
StackOverflowError user/b (form-init5239882861501900074.clj:1)
For more description of the problem see the ticket at http://dev.clojure.org/jira/browse/CLJ-1279.

Clarification on Clojure evaluation

In Clojure for the Brave and True, chapter 8, a function called if-valid is proposed (then rejected) to abstract away the repetitive parts of validation checks:
(defn if-valid
[record validations success-code failure-code]
(let [errors (validate record validations)]
(if (empty? errors)
success-code
failure-code)))
The author explains that the function in its above state won't work as success-code and failure-code will be evaluated on each if-valid call. My understanding is, that the if function's test will return true or false, and that dictates whether the success or failure code runs. Please can someone explain how both then and else portions of the if will be evaluated for each if-valid call?
Suppose that this function is used as follows:
(if-valid my-data validators
(println "Data accepted")
(throw (Exception. "Bad data :(")))
This is no good, because function arguments must be evaluated before they can be passed to the function. So, the side effects of first printing "Data accepted" and then throwing an exception will both be performed every time, before this function gets a chance to run validations at all.

Why one function got from a macro works while another cannot be compiled?

Here's snippet:
(defmacro produce-constantly-fn []
(constantly :value))
(defmacro produce-fn []
(fn [& args] :value))
(defn test-fn []
((produce-fn)))
;; during evaluation of form below it throws:
;; java.lang.IllegalArgumentException
;; No matching ctor found for class clojure.core$constantly$fn__4614
(defn test-constantly-fn []
((produce-constantly-fn)))
Why last function cannot be compiled? The snippet can be considered as some sort macros abuse, but anyway...
I assume you defined your macro body without quoting and you are curious why it results in such a weird error message. If you really meant to define a macro for calling (constantly :value) then you should use quoting and it will work:
(defmacro produce-constantly-fn []
`(constantly :value))
(defn test-constantly-fn []
((produce-constantly-fn)))
=> #'user/test-constantly-fn
(test-constantly-fn)
=> :value
Now going back to your case without quoting. It looks really interesting and mysterious so I did some digging. These are my findings:
When you define a macro:
(defmacro produce-constantly-fn []
(constantly :value))
it will just create a function named produce-constantly-fn and mark it as a macro (it's still a Clojure function).
When you look into the implementation of constantly you will find (docs and meta omitted):
(defn constantly [x]
(fn [& args] x))
Under the hood it will compile to a closure object which will implement IFn and will have a single constructor parameter to close over x parameter. Something like this in Java code:
public class clojure.core$constantly$fn__4614 {
private final Object x;
public clojure.core$constantly$fn__4614(Object x) {
this.x = x;
}
public Object invoke(...) {
return x;
}
// other invoke arities
}
Now when you have following sexp:
(defn test-constantly-fn []
((produce-constantly-fn)))
I noticed that Clojure reader evals (produce-constantly-fn) which should return just a function object (produced by calling (constantly :value)) but I found in debugger that it produces clojure.core$constantly$fn__4614. symbol instead (notice the . at the end of the symbol - it is a Java interop form for calling a constructor). It looks like a function object/value gets somehow converted to a symbol representing its constructor call. I could find that the function value gets converted into Compiler$InvokeExpr object containing references to the compiled class name which is probably somehow converted into the symbol.
The reader tries to resolve clojure.core$constantly$fn__4614. further. It gets transformed by the reader into a call to clojure.core$constantly$fn__4614clojure.core$constantly$fn__4614 class constructor with no parameter.
As you have seen above the constructor of that class requires exactly one constructor thus the compilation fails (in clojure.lang.Compiler.NewExpr constructor body) with:
java.lang.IllegalArgumentException: No matching ctor found for class clojure.core$constantly$fn__4614
I am not sure why the Clojure reader transforms the function value to a symbol with constructor call interop form and causing such behaviour thus I presented only the direct cause of the error message and not the root cause why your code doesn't work. I guess it might be a bug or it was a conscious design decision. From the macro authors it would be better to fail fast and learn that the return value of a macro is not a valid code data but on the other hand it might be very difficult or impossible to determine if the returned data is a valid code or not. It's worth checking on Clojure mailing list.
I think what is going on is: macros resolve before functions.
So if you call macroexpand-1 on the function you get:
(def test-constantly-fn (clojure.core/fn ([] ((produce-constantly-fn)))))
Thus ((produce-constantly-fn)) is called before function and gives listed error.

Why doesn't the Clojure Compiler throw an error for an incorrect type hint?

Assumptions:
I get that type hints are about performance optimisations, and not type checking. I'm trying to work out when a performance optimisation is ineffective.
Suppose I have the following code:
(ns test.core)
(defrecord SquarePeg [width length])
(defrecord RoundHole [radius])
(def square-peg (SquarePeg. 5 50))
(defn insert-peg [^test.core.RoundHole peg]
(println "insert-peg inserted with: " peg))
(defn -main
"Insert a square peg in a round hole"
[& args]
(insert-peg square-peg))
When I run it I get:
insert-peg inserted with: #direct_linking_test.core.SquarePeg{:width 5, :length 50}
Now I expect this to have some kind of indication that the type hint was wrong, but it doesn't.
Now I'm taking a look at the Clojure Compiler code - and I'm seeing the following hint handling code:
hinted arg list
emit static value from hinted fields
get the class of the hinted field
if hinted match method
But I'm not seeing the bit where it handles the type hint failure.
My question is: Why doesn't the Clojure Compiler throw an error for an incorrect type hint?
Type hints mostly[1] only affect code that would otherwise use reflection -- i.e., interop code.
Since your insert-peg function doesn't do any interop, there's no use for the type hint and it's ignored.
Type errors happen when your type hint caused the clojure compiler to write bytecode for calling the method of one type, but at runtime the instance turns out to be another type.
[1] see exception in Alex's comment below

How to return a value on Clojure try/catch

I've a function in Clojure where I want to return true when an exception occurs, just like the code below;
(try
(code)
(catch Exception e true)))
However the approach above gives me the following error:
ClassCastException java.lang.Boolean cannot be cast to clojure.lang.IFn signal.message/read? (message.clj:12)
It only works if a put a function instead:
(try
(code)
(catch Exception e (= 1 1)))
Any way to return a value on Clojure catch?
Test your catch using:
(try
(throw (RuntimeException.))
(catch Exception e true)))
Running this code would return true.
What you've written in your question is exactly right: that try/catch as written returns true if (code) throws an exception. The problem is elsewhere: either the code around this try/catch has too many parentheses, or you have pasted code that does not match the real code you are running.