Can someone explain the behavior in the Clojure code below?
I don't get it.
Does Clojure somehow replace or "optimize" function arguments? Why does calling a function with a single nil argument result in an ArityException?
(defn foo [bar] (reduce #(%1) bar))
(foo nil)
-> ArityException Wrong number of args (0) passed to: test$foo$fn clojure.lang.AFn.throwArity (AFn.java:437)
See (doc reduce):
[...]
If coll contains no
items, f must accept no arguments as well, and reduce returns the
result of calling f with no arguments.
[...]
Here coll is nil, which is effectively being treated as a collection containing no items (as it usually is in similar contexts), and f is #(%1).
Thus #(%1) is being called with no arguments and ends up throwing the exception you see.
Related
I am pretty new with Clojure language.
While reading about Clojure functions, I find the example #([%]). So I try to use it as follows:
(def test1 #([%]))
(test1 5)
As a result, I get the following error:
ArityException Wrong number of args (0) passed to: PersistentVector clojure.lang.AFn.throwArity (AFn.java:429)
which seems to be that it is trying to invoke the array I wanted to return.
After digging a while, I find a solution as follows:
(def test1 #(-> [%]))
(test1 5)
I would have some questions:
Why doesn't the #([%]) work? What did I do with the expression #([x])?
In the correct example I am using the thread-first macro. Based on its documentation, it is used to pass an argument to the next function, e.g. (-> x (+ 1)). In this case I do not even have a function to pass to; *what is the next function in this context? I can not realize why it solved my issue
Question 1
The syntax #([%]) translates into: "Create a function that when called will evaluate the expression ([%]) with % being the first (and only) argument passed to the function". This expression has the syntax of a function call with [%] being the function to be called. You can see what goes on using a macroexpand:
(macroexpand '#([%]))
;; => (fn* [p1__6926#] ([p1__6926#]))
The class of persistent vectors in clojure is clojure.lang.PersistentVector. They implement the IFn interface for arity 1, so that you can treat the vector as a function mapping an index to an element. But they do not implement arity 0, which is what you are trying to call. In other words, your code does not work:
(def test1 #([%]))
(test1 5) ;; ERROR
However, if you would pass the argument 0 to your function [%], you would get back the element:
(def test1 #([%] 0))
(test1 5)
;; => 5
Do you see what happens? However, for the thing you are trying to do, there is a better way: The [a b c] syntax is just sugar for calling (vector a b c). So to get something that works, you can just do
(def test1 vector)
(test1 5)
;; => [5]
Question 2
The thread-first macros has the syntax of (-> x f0 f1 f2 ...) where x is the initial value and f0, f1 and so on are function calls with their first argument left out to be replaced by the value that is being piped through. Again we can use macroexpand to understand:
(macroexpand '(-> x f0 f1 f2))
;; => (f2 (f1 (f0 x)))
But in your case, the function calls are left out. To analyze your second example, we need to use clojure.walk/macroexpand-all for a full expansion, because we have nested macros:
(clojure.walk/macroexpand-all '#(-> [%]))
;; => (fn* [p1__6995#] [p1__6995#])
although, we can also look at it one step at a time:
(macroexpand '#(-> [%]))
;; => (fn* [p1__7000#] (-> [p1__7000#]))
(macroexpand '(-> [p1__7000#]))
;; => [p1__7000#]
So to answer your question: There is no next function in (-> [%]). The number of next functions can be any non-negative number, including zero, which is the case with (-> [%]).
#Rulle gives an exhaustive explanation of the details.
May I point out the most important part? Your reference from Clojure.org says:
;; DO NOT DO THIS
#([%])
So, don't do that! It is a silly trick that will only cause confusion & pain. Why would you want that???
In a riddle I have to complete the following expressions in order to evaluate to true. There must be one single insertion, which fits to all of them.
(= 15 (reduce __ [1 2 3 4 5]))
(= 0 (reduce __ []))
(= 6 (reduce __ 1 [2 3]))
The third expression provides a start value. Hence my replacement cannot provide another one.
This function would pass the first and the third truth test:
#(+ %1 %2)
However, the second expression yields the following error:
clojure.lang.ArityException: Wrong number of args (0) passed to (...my function id)
It looks like usually reduce calls the given function only if the length of start value + collection is bigger than 2. If this length is 0, like in the case above, it is called as well - with 0 arguments.
Does anyone have a hint how to carry on here?
From the comments, the solution is clearly +, but maybe it's valuable to look at it in some detail to see why. As it turns out, there's a lot to it.
First, let's look at reduce to see what the requirements are:
(defn reduce
"f should be a function of 2 arguments. If val is not supplied,
returns the result of applying f to the first 2 items in coll, then
applying f to that result and the 3rd item, etc. If coll contains no
items, f must accept no arguments as well, and reduce returns the
result of calling f with no arguments. ..."
...
([f coll]
(if (instance? clojure.lang.IReduce coll)
(.reduce ... coll f)
...))
([f val coll]
(if (instance? clojure.lang.IReduceInit coll)
(.reduce ... coll f val)
...)))
This is a multi-arity function that either takes a function and a collection, or a function, initial value and a collection.
+ is also a multi-arity function that behaves depending on how many arguments you pass to it. The source below (edited for the parts we care about), shows reduce is satisfied by 0-arity and 2-arity.
(defn +
"Returns the sum of nums. (+) returns 0..."
...
([] 0)
...
([x y] (. clojure.lang.Numbers (add x y)))
...
Clearly (reduce + []) calls the first clause and returns 0. Test 2 is explained.
This works for the first test by applying the add function to each pair of Numbers, which happens in the Java internals for Clojure, in a tight for loop.
The final test works exactly like the first, except it calls the [v val coll] implementation of reduce. This applies a slightly different internal function, but with the same effect.
Notes
[1]: IFn is the Java interface for clojure functions
I get the following weird behaviour from a clojure function: When I call it with one argument it seems as if it is a function, when I call it without arguments it appears to be a symbol. Any ideas how this can be?
this is what happens in the interpreter:
=> (input-updatef -1)
ArityException Wrong number of args (1) passed to: modelingutils/create-process-level/input-updatef--2954 clojure.lang.AFn.throwArity (AFn.java:429)
and when I try calling it without any argument:
=> (input-updatef)
ArityException Wrong number of args (0) passed to: Symbol clojure.lang.AFn.throwArity (AFn.java:429)
Thx!
Answering "how this can be":
user=> (defn foo [] ('foo))
#'user/foo
user=> (foo 1)
ArityException Wrong number of args (1) passed to: user/foo clojure.lang.AFn.throwArity (AFn.java:429)
user=> (foo)
ArityException Wrong number of args (0) passed to: Symbol clojure.lang.AFn.throwArity (AFn.java:429)
Of course your input-updatef situation may be more subtle, but it is at least clear that
either the actual input-updatef function has no unary overload or it has one, but when you call it it ends up calling a function that has no unary overload with just one argument;
it has a nullary overload;
calling the nullary overload results in a call to a symbol with no arguments.
Also, based on the modelingutils/create-process-level/input-updatef--2954 part of your error message it seems to me that input-updatef might be a "local function" – created using letfn or introduced as the value of a let binding – returned at some point from a function called create-process-level. Here's an example of what that could look like:
user=> (defn foo
([]
('foo))
([x]
(letfn [(f [])]
(f x))))
#'user/foo
user=> (foo 1)
ArityException Wrong number of args (1) passed to: user/foo/f--4 clojure.lang.AFn.throwArity (AFn.java:429)
user=> (foo)
ArityException Wrong number of args (0) passed to: Symbol clojure.lang.AFn.throwArity (AFn.java:429)
Using
(defn foo
([]
('foo))
([x]
(let [f (fn [])]
(f x))))
would have the same effect.
Thanks, both answers helped.
I did not post the definition, because it contained a complex macro..
The problem was that I called the macro from a normal function and supplied an argument (an ff function) to this macro from the argument list of the calling function. This ff was interpreted as a symbol at macro evaluation time -- this is what caused the strange behaviour.
Solution: I changed the outer calling function into a macro, and unquoted ff in the argument list of the called macro.
I am a little confused by the clojure instance? function. It seems quite happy to take a single argument. So
(instance? String)
works fine, but always returns false.
Am I missing something here? I've done this twice in two days, and both times it took me a quite a long time to debug (yes, I agree, to make the mistake once might be regarded as misfortune, but twice looks like carelessness).
Why doesn't it break, with an arity error?
Note added later:
As of Clojure 1.6 this has been fixed!
http://dev.clojure.org/jira/browse/CLJ-1171
Interesting... even though instance? is defined in core.clj, it appears that there is special handling built in to clojure.lang.Compiler for (instance?) forms.
Compiler.java, line 3498:
if(fexpr instanceof VarExpr && ((VarExpr)fexpr).var.equals(INSTANCE))
{
if(RT.second(form) instanceof Symbol)
{
Class c = HostExpr.maybeClass(RT.second(form),false);
if(c != null)
return new InstanceOfExpr(c, analyze(context, RT.third(form)));
}
}
I interpret that to mean that, when you compile/evaluate an (instance?) form, the function defined in core.clj is ignored in favor of the hard-wired behavior, which does interpret a missing second argument as nil. I'm guessing this is done for performance reasons, as a sort of in-lining.
Apparently this special handling only applies in certain cases (and I'm not familiar enough with the compiler to know what they are). As illustrated by Ankur's answer, there are ways of calling instance? that cause the function defined in core.clj to be invoked.
I think it is a bug. If you define a new version of instance?, e.g.
(def
^{:arglists '([^Class c x])
:doc "Evaluates x and tests if it is an instance of the class
c. Returns true or false"
:added "1.0"}
foo? (fn foo? [^Class c x] (. c (isInstance x))))
you will get the expected exception
user=> (foo? String "bar")
true
user=> (foo? String 1)
false
user=> (foo? String)
ArityException Wrong number of args (1) passed to: user$foo-QMARK- clojure.lang.AFn.throwArity (AFn.java:437)
If you look at the instance? code you will see that the method isInstance of Class is called:
(def
^{:arglists '([^Class c x])
:doc "Evaluates x and tests if it is an instance of the class
c. Returns true or false"
:added "1.0"}
instance? (fn instance? [^Class c x] (. c (isInstance x))))
Looks like under the hood, nil (or false) is considered as the default value for x parameter when passed to the isInstance and that returns false.
Hmm....interesting... all the below calls fails (which is how it is supposed to be):
user=> (.invoke instance? String)
ArityException Wrong number of args (1) passed to: core$instance-QMARK- clojure.lang.AFn.throwArity (AFn.java:437)
user=> (instance? (type ""))
ArityException Wrong number of args (1) passed to: core$instance-QMARK- clojure.lang.AFn.throwArity (AFn.java:437)
user=> (apply instance? String [])
ArityException Wrong number of args (1) passed to: core$instance-QMARK- clojure.lang.AFn.throwArity (AFn.java:437)
user=> (#'instance? Long)
ArityException Wrong number of args (1) passed to: core$instance-QMARK- clojure.lang.AFn.throwArity (AFn.java:437)
Event creating a new instance of "instance?" function object works as it is supposed to work:
user=> (def a (.newInstance (aget (.getConstructors (type instance?)) 0) (into-array [])))
#'user/a
user=> (a String)
ArityException Wrong number of args (1) passed to: core$instance-QMARK- clojure.lang.AFn.throwArity (AFn.java:437)
user=> (a String "")
true
In Clojure what is the idiomatic way to test for nil and if something is nil then to substitute a value?
For example I do this a lot:
let [ val (if input-argument input-argument "use default argument")]
: but I find it repetitive having to use "input-argument" twice.
just use or:
(or input-argument "default")
Alex's suggestion of "or" is indeed the idiomatic way to rewrite your example code, but note that it will not only replace nil values, but also those which are false.
If you want to keep the value false but discard nil, you need:
(let [val (if (nil? input-argument) "use default argument" input-argument)]
...)
If you only bind the variable to do get the right value and not to use it twice there is a other way you can do it. There is a function in core called fnil.
You call fnil with the function you want to call and the default argument. This will return a function that will replace nils with the default value you provided.
The you can do one of the things depending on what you want. Creat a local function.
(let [default-fn (fnil fn-you-want-to call "default-argument")]
(default-fn input-argument))
In somecases (where you always have the same default argument) you can move to logic to do this out of your code and put it where to original function was (or wrap the function in case it in a other library).
(defn fn-you-want-to-call [arg] ....)
(def fn-you-want-to-call-default (fnil fn-you-want-to-call "default-argument"))
Then in your code its reduced to just
(fn-you-want-to-call-default input-argument)
More you can find here:
http://clojuredocs.org/clojure_core/clojure.core/fnil
When the expected value is a boolean I recommend using an util fn.
(defn- if-nil [default val]
(if (nil? val)
default
val))
(if-nil true (possible-false input))