there seems to be a limit to the number of parameters a clojure function can take.
When defining a function with more than 20 parameters I receive the following:
#<CompilerException java.lang.RuntimeException: java.lang.RuntimeException: java.lang.Exception: Can't specify more than 20 params (NO_SOURCE_FILE:0) (NO_SOURCE_FILE:0)>
Obviously this can be avoided, but I was hitting this limit porting the execution model of an existing DSL to clojure, and I have constructs in my DSL like the following, which by macro expansion can be mapped to functions quite easily except for this limit:
(defAlias nn1 ((element ?e1) (element ?e2)) number
"@doc features of the elements are calculated for entry into
the first neural network, the result is the score computed by the latter"
(nn1-recall (nn1-feature00 ?e1 ?e2) (nn1-feature01 ?e1 ?e2) ... (nn1-feature89 ?e1 ?e2)))
which is a DSL statement to call a neural network with 90 input nodes. Can work around it of course, but was wondering where the limit comes from. Thanks.
( do expr*)Evaluates the expressions exprs in order and returns the value of the last. If no expressions are supplied, returns nil .
This is a perfect opportunity to enforce encapsulation to avoid drowning the client in board-implementation details. Clojure has closures, and closures are an excellent way to group functions (Crockford 2008) with their supporting data.
Clojure doesn't have an explicit return statement. Instead, Clojure will return the result of the last form evaluated in a function (a form is just a valid piece of code).
Clojure let is used to define new variables in a local scope. These local variables give names to values. In Clojure, they cannot be re-assigned, so we call them immutable.
First of all, the limit only applies to required positional arguments; you can always use the variable arity case (& more-args
in the function's signature) to handle as many arguments as you want to:
(defn foo [& args]
(count args))
(foo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25)
;; returns 25
In fact, at first glance, & args
is likely to be exactly the right solution to your problem. (E.g. you'll be able to map a function over your input nodes collected into a sequence, loop
/ recur
over said sequence etc. -- tends to make more sense with a large number of similar items than assigning separate names to each one of them.
(Note that I don't pretend to know the nature of the specific DSL you're transcribing into Clojure or the kind of problems you're dealing with, just suggesting points which might be of interest to you. If you've got a really funky situation where this doesn't seem to apply, maybe you can provide more details and we'll see if someone here can offer some useful hints for dealing with it in Clojure.)
For the sake of completeness, you can add the & args
bit to a function which takes its first 19 arguments as required positional args:
(defn bar [a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 & as]
(+ 19 (count as)))
(bar 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25)
;; returns 25
Note that if you specify 20 positional args and the &
arg on top of that, apparently weirdness will ensue.
As for the rationale, I believe this has to do with the fact that the JVM can dispatch to a method by arity very efficiently, so the clojure.lang.Fn
class has the invoke
method overloaded for arities up to 20. I'm not entirely sure if it could go higher than that, but I suppose this isn't something people require that often... I mean, I certainly find any API specifying over 20 positional arguments to a function a bit suspect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With