3

I'm very new to Clojure and asking myself, how the arity of an anonymous function is defined/deducted.

Please consider the following poor-man count function:

(reduce #(+ 1 %1) 0 '(1 2 3 55))

Running it with clojure I'm getting the following error message:

ArityException Wrong number of args (2) passed to: user/eval1157/fn--1158 clojure.lang.AFn.throwArity (AFn.java:429)

However, it works fine with JavaScript-Clojure and returns 4 as desired (you can execute the command here).

Changing the command by exchanging %1->%2 to

(reduce #(+ 1 %2) 0 '(1 2 3 55))

would compile on both versions (but no longer work as count). In this case seemingly, it can be deduced from %2 that there are at least two arguments.

So which version of Clojure is right? Am I allowed to feed an arbitrary amount of arguments to an anonymous function defined via #(...) or only as many as mentioned inside of this function?

Should it be considered a bug in JavaScript-Clojure?

Edit: As have been explained in comments and in answer, that this is just the way how JavaScript works. There are differences to Java-Clojure, which are documented here, in particular:

There is currently no runtime enforcement of arity when calling a fn

ead
  • 32,758
  • 6
  • 90
  • 153
  • i guess that is because `#()` syntax is just a reader sugar for plain `(fn )`, and the appropriate fn arity is generated by the highest `%n` item, so yes, if you want the function with arity `n` you *have to* use the parameter `%n` somewhere. – leetwinski Aug 11 '17 at 09:31
  • 1
    then the argument to `reduce` is the function of 2 parameters, and here comes the difference: in your case the compiler generates `(fn [x] ...)` for both clj and cljs, but in case of cljs further it is converted to js function: and in js the following is totally ok for the compiler: `(function(x) { return x + 1})(1,2,3,4,5)`, while in java the arity should be exactly matched. So while it is possible in js, it is obviously a code smell. – leetwinski Aug 11 '17 at 09:38
  • @leetwinski But is there a something like a standard for clojure, which would say what is the right behavior of the interpreter? You see, I come from C++:) – ead Aug 11 '17 at 09:48
  • @ead I guess the "standard truth" lies in Clojure API docs for `reduce`: [f should be a function of 2 arguments](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/reduce) (same goes for [cljs API](http://cljs.github.io/api/cljs.core/#reduce)). The fact that you can give a cljs/js anon function with one argument is an implementation artefact like @leetwinski described. – Toni Vanhala Aug 11 '17 at 10:50
  • @ead , since there is no described standard at all, the JVM implementation and it's documentation is the de-facto standard. While the other implementations are just it's ports, and suffer from whatever incompatibilities underlying platform brings – leetwinski Aug 11 '17 at 12:43

1 Answers1

6

The parameters in the resulting function is determined by the highest numbered used parameter. Thus by using %2 both become a 2 arity function, but since you only use %1 (or %) it becomes a one arity function in both versions of Clojure.

In JavaScript you can pass as many arguments as you'd like and even pass fewer than the parameter list. Variables without matching argument become undefined and arguments without defined parameters are simply not bound and available in the function. ClojureScript would have to add argument checking in each function in order to get the same behavior as in Clojure. It would be a bug if this is the specified behavior. Many transpiled languages does this.

The solution for your example is to not use the short version but rather fn:

(reduce (fn [a _] (+ 1 a)) 0 '(1 2 3 55))
Sylwester
  • 47,942
  • 4
  • 47
  • 79