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.
Related
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.
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.
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
Is there a way to use the reader with function values, e.g:
(read-string (pr-str +))
RuntimeException Unreadable form clojure.lang.Util.runtimeException
(Util.java:219)
?
As you might already know the output for (pr-str +) is not valid Clojure code that the reader can parse: "#<core$_PLUS_ clojure.core$_PLUS_#ff4805>". The output for function values when using the functions pr, prn, println and such, is intentionally wrapped around the #< reader macro that dispatches to the UnreadableReader, which throws the exception you are seeing.
For the example you provided you can use the print-dup function that works for basic serialization:
(defn string-fn [f]
(let [w (java.io.StringWriter.)]
(print-dup f w)
(str w)))
(let [plus (read-string (string-fn +))]
(plus 1 2))
The serialization done for the + function is actually generating the call to the class' constructor:
#=(clojure.core$_PLUS_. )
This only works of course if the class is already compiled in the Clojure environment where you are reading the string. If you serialized an anonymous function, saving it to a file and then reading it back in, when running a new REPL session, it will most likely not work since the class name for each anonymous function is different and depends on Clojure internals.
For arbitrary functions things get a lot more complicated. Sharing the source code might not even be enough, the function could rely on the usage of any number of other functions or vars that only exist in the source environment. If this is what you are thinking of doing, maybe considering other approaches to the problem you are trying to solve, will eliminate the need to serialize the value of arbitrary functions.
Hope it helps,
If you only need the name, can you just send the symbol with (name '+).
But generally speaking, it is a bad idea to use clojure read, if you want to read it back, as clojure's reader might execute some code in the process. Maybe have a look at the edn reader : clojure.edn/read-string
But maybe you just need to convert the string back to a symbol, in which case the (symbol name) function would be enough.
I have some Clojure code that is trying to interop through a couple layers of Java code (in this case, java.nio.Path by way of java.nio.file.WatchEvent<?>:
(defn unroll-event
[^WatchEvent event]
{ :kind (.kind event)
:context (.context event)
:path (-> event .context .toAbsolutePath .toString)})
In this code, I have type hinted event, so I would think it should be able to figure out what .context is supposed to return, and as such, be able to figure out what .toAbsolutePath and .toString do. I think in this case, since .context is defined has returning a generic type T, I am wondering if I can type hint the call to .context. I've tried just prepending ^java.nio.file.Path to .context, and ^Path and ^String to .toAbsolutePath and toString, respectively, but I still get the warnings:
Reflection warning, junkcode/core.clj:28 - reference to field toAbsolutePath can't be resolved.
Reflection warning, junkcode/core.clj:28 - reference to field toString can't be resolved.
Is there something I can do in this case? Is it because -> is a macro and there are special rules for type hinting within that?
(-> x .blah ^String .bar) expands to, basically, (^String .bar (.blah x)), which is clearly not where you want the hint. The point is that type-hinting does not have special behavior in any context (eg, macros): it's just metadata applied to the source-code symbols. In your example ->, there is no place that you can put metadata on the input form that will cause it to be where you want in the output form. So you need to write some other form, like (-> ^Path (.context event) .toAbsolutePath str), for example.
Also, Clojure's inferencer doesn't know anything about generic types, so a method-returning-T gets treated as a method-returning-Object, which explains why you need to hint at all in here.
I don't know if this has always been the case, but in Clojure 1.4.0, 1.5.1, and 1.6.0, you can type-hint at any point in -> as long as you use parentheses:
user=> (set! *warn-on-reflection* true)
true
user=> (fn [^java.nio.file.WatchEvent e]
(-> e ^java.nio.file.Path .context .toAbsolutePath))
Reflection warning, /private/var/folders/9_/wdph6m796zzc8trzcbtcmhrn5bjpt0/T/form-init8364673644863044068.clj:1:35 - reference to field toAbsolutePath on java.lang.Object can't be resolved.
#<user$eval1995$fn__1996 user$eval1995$fn__1996#8128f39>
user=> ; but no warning if we do
user=> (fn [^java.nio.file.WatchEvent e]
(-> e ^java.nio.file.Path (.context) .toAbsolutePath))
#<user$eval1999$fn__2000 user$eval1999$fn__2000#4747e32a>
The only difference is the parens around .context.