<div>As a presage to this email, I spend a lot of time explaining, and my question doesn't really come until the very end. Also, by the time I got to the end of writing , I managed to accidentally solve my original question, and now I'm curious whether anybody can tell me why what I did solved the problem.</div>
<div><br></div>I've been trying to work on getting the arities for all the clojure functions, and what types of arguments each arity for each function will accept. I'm on the second part now, and I plan on going about it by trying all the possible combinations of argument types, and returning the types of the ones that don't error. So I decided to make a general-purpose macro for returning a list of all the possible combinations of applying a function f to a series of collections--using each value from the first collection as the first argument to f, and each from the second collection as the second argument to f, etc. If you understand how the "for" loop comprehension works (if not, maybe the examples <a href="http://clojuredocs.org/clojure_core/clojure.core/for" target="_blank">here</a> would help), this is an example of how the concept would work, using + as the function, [1 2 3] as the first collection, and [10 20] as the second collection:<div>
<br></div><div>(for [elem1 [1 2 3] elem2 [10 20]]</div><div> (+ elem1 elem2))</div><div>>> (11 21 12 22 13 23)</div><div><br></div><div>This could be generalized to:</div><div><br></div><div>(defn combins</div><div>
[f c1 c2]</div><div> (for [elem1 c1 elem2 c2]</div><div> (f elem1 elem2))</div><div><br></div><div>(combins + [1 2 3] [10 20])</div><div>>> (11 21 12 22 13 23)</div><div><br></div><div>The problem, which may be apparent after looking at it, is that something like the "combins" function above is not well suited to a long series of collections (frankly, for what I plan on using it for, this is not a necessary feature--but still). In order to do a combination of + with [0 128], [0 64], [0 32], [0 16], [0 8], [0 4], [0 2], and [0 1] (which should return a list of all the positive integers that are less than 256), I would have to create a function that looked something like:</div>
<div><br></div><div>(defn combins2</div><div> [f c1 c2 c3 c4 c5 c6 c7 c8]</div><div> (for [elem1 c1 elem2 c2 elem3 c3 elem4 c4 elem5 c5 elem6 c6 elem7 c7 elem8 c8]</div><div> (f elem1 elem2 elem3 elem4 elem5 elem6 elem7 elem8))</div>
<div><br></div><div><div>(combins + [0 128] [0 64] [0 32] [0 16] [0 8] [0 4] [0 2] [0 1])</div><div>>> (1 2 3 4 5 6 7 8 9 10 ... 250 251 252 253 254 255)</div></div><div><br></div><div>I figured that creating a macro would be a more elegant way to handle a theoretically limitless series of collections. A quick introduction to macros: macros are functions that generate code as output. When writing a macro, you use symbols like `, ~, and ~@. The ` symbol (called the backquote symbol) is used a lot like the ' symbol (but they are not the same thing), to denote something that should be returned as is, not evaluated. The ~ symbol (called the unquote symbol) is used to denote something that should be evaluated and then that return value should be inserted as is. The ~@ symbol (called the unqoute splice symbol, or something) is pretty cool, it returns the elements inside of what it evaluates to. Both of the unquote symbols have to be used inside a backquoted block of code. Here's an example of how it works:</div>
<div><br></div><div>(def example-collection [1 2 3])</div><div><br></div><div>~example-collection</div><div>>> #<CompilerException java.lang.IllegalStateException: Var clojure.core/unquote is unbound. (NO_SOURCE_FILE:0)></div>
<div><br></div><div>~@example-collection</div><div>>> #<CompilerException java.lang.IllegalStateException: Var clojure.core/unquote-splicing is unbound. (NO_SOURCE_FILE:0)></div><div><br></div><div>`(list 0 example-collection 4 5 6)</div>
<div>>> (clojure.core/list 0 user.core/example-collection 4 5 6)</div><div><br></div><div>`(list 0 ~example-collection 4 5 6)</div><div>>> (clojure.core/list 0 [1 2 3] 4 5 6)</div><div><br></div><div>`(list 0 ~@example-collection 4 5 6)</div>
<div>>> (clojure.core/list 0 1 2 3 4 5 6)</div><div><br></div><div>Note the difference between the above examples versus using ' instead of `, which just returns exactly what comes after it.</div><div><br></div>
<div><div>'(list 0 example-collection 4 5 6)</div><div>>> (list 0 example-collection 4 5 6)</div><div><br></div><div>'(list 0 ~example-collection 4 5 6)</div><div>>> (list 0 ~example-collection 4 5 6)</div>
<div><br></div><div>'(list 0 ~@example-collection 4 5 6)</div><div>>> (list 0 ~@example-collection 4 5 6)</div></div><div><br></div><div>In my case I want to generate a "for" with a variable number of bindings. Essentially, instead of passing a "hard-coded" vector of variables and bindings to the "for" list comprehension, I'm passing a vector that has been derived. After a lot (a lot) of attempts, I managed to come up with something elegant and concise, using an infinite lazy list of "anonymous" symbols (in clojure, I believe it is idiomatic to use an underscore to denote a variable that is being defined whose name is not important). For people unfamiliar with macros--I'm kind of jumping right into the deep-end here, apologies:</div>
<div><br></div><div><div>(def infinite-symbol-list</div><div> (map #(symbol (format "_%d" %)) (iterate inc 1)))</div></div><div><br></div><div>(take 4 infinite-symbol-list)</div><div>>> (_1 _2 _3 _4)</div>
<div><br></div><div><div>(defmacro combins</div><div> [f & colls]</div><div> `(for ~(vec (interleave infinite-symbol-list colls))</div><div> (~f ~@(take (count colls) infinite-symbol-list))))</div></div><div><br>
</div><div>(combins + [0 128] [0 64] [0 32] [0 16] [0 8] [0 4] [0 2] [0 1])</div><div>>> (1 2 3 4 5 6 7 8 9 10 ... 250 251 252 253 254 255)</div><div><br></div><div>I'll try to quickly explain the macro I wrote. When you have "& var-name" at the end of an argument list for a function, it makes a list out of the remaining arguments and stores that in "var-name". For example:</div>
<div><br></div><div>(defn example-fn</div><div> [a & more]</div><div> (list a more))</div><div><br></div><div>(example-fn 1 2 3 4 5 6 7 8)</div><div>>> (1 (2 3 4 5 6 7 8))</div><div><br></div><div>The "interleave" function takes two collections, and then returns a list of (first coll1), (first coll2), (second coll1), (second coll2), etc. until one of the collections runs out of elements. Here, I am interleaving the infinite symbol list (_1 _2 _3 ...) with the "colls"--which should be a collection of the series of collections passed to combins. Let's represent colls as (c1 c2 c3 ... cN) where cN is the last collection in the series. Interleave these will return (_1 c1 _2 c2 _3 c3... _N cN). Calling "vec" on this will return [_1 c1 _2 c2 _3 c3 ... _N cN]. Since ~ will return the result of evaluating what is after it, `(for ~(vec (interleave infinite-symbol-list colls)) ...) will return (clojure.core/for [_1 c1 _2 c2 _3 c3 ... _N cN] ...) which is exactly what I want. After this, I want to call f using _1 as the first argument, _2 as the second argument, etc. I have to do ~f to ensure that I am using "the function inside f" (e.g. "+") as opposed to "f itself" (which does not exist). Then, because infinite-symbol-list is infinite (shocker, I know) we have to make sure to take only what we need of it. Doing (take (count colls) infinite-symbol-list) will return (_1 _2 _3 ... _N), where N is the number of collections in the series (i.e. "(count colls)"). Since ~@ will return the elements inside of the return value of the stuff after it, ~@ should return _1 _2 _3 ... _N. So, in the example that f is +, (~f ~@(take (count colls) infinite-symbol-list)) will return (+ _1 _2 _3 ... _N). Great. Still using + as the example f, the macro as a whole will return and then evaluate:</div>
<div><br></div><div>(clojure.core/for [_1 c1 _2 c2 _3 c3 ... _N cN]</div><div> (+ _1 _2 _3 ... _N))</div><div><br></div><div>I hope all of that made sense. So...my question. Originally, I told you that I was planning on testing each arity of each function, using all the possible combinations of argument types, to discover what argument types each arity would accept. If I were just trying to test a single list of argument types on a single function, I could do this:</div>
<div><br></div><div><div>(def arg-types</div><div> {\t :character</div><div> [1 2 3 4] :collection</div><div> + :function</div><div> 42 :number</div><div> #"ab" :regex</div><div> #{1 2 3 4} :set</div>
<div> "Text is in here." :string})</div></div><div><br></div><div><div>(defn try-args</div><div> [f & elems]</div><div> (try (do (apply f elems) (cons f (map test-types elems)))</div><div> (catch Exception e)))</div>
</div><div><br></div><div>(try-args + \t)</div><div>>> nil</div><div><br></div><div>(try-args + 42)</div><div>>> (#<core$_PLUS_ clojure.core$_PLUS_@507726> :number)</div><div><br></div><div>(try-args + 42 \t)</div>
<div>>> nil</div><div><br></div><div>(try-args + 42 42)</div><div>>> (#<core$_PLUS_ clojure.core$_PLUS_@507726> :number :number)</div><div><br></div><div>All that #<core$_PLUS...> nonsense is equivalent to +. So it makes sense to me that I could do the following to test what argument types a 1-arity call to + or - accepts:</div>
<div><br></div><div>(combins try-args [+ -] (keys arg-types))</div><div>>> #<ClassCastException java.lang.ClassCastException: java.lang.Character cannot be cast to clojure.lang.IFn></div><div><br></div><div>But I can't, and it looks like it's trying to evaluate the result of (keys (arg-types)), and I don't understand why, but if I cast the result into a vector that should fix the problem:</div>
<div><br></div><div>(combins try-args [+ -] (vec (keys arg-types))</div><div>>> (nil nil nil nil nil nil nil nil nil nil nil nil nil nil)</div><div><br></div><div>So it "works" now, but it's not returning what it "should" be. Here's the equivalent of what I expect the macro to return and evaluate:</div>
<div><br></div><div>(for [_1 [+ -] _2 (vec (keys arg-types))]</div><div> (try-args _1 _2))</div><div>>> (nil nil nil (#<core$_PLUS_ clojure.core$_PLUS_@507726> :number) nil nil nil nil nil nil (#<core$_ clojure.core$_@ac9cbe> :number) nil nil nil)</div>
<div><br></div><div>Wow, just right now, at this point in writing this email, I just fixed the problem. If instead of:</div><div><br></div><div><div>(defmacro combins</div><div> [f & colls]</div><div> `(for ~(vec (interleave infinite-symbol-list colls))</div>
<div> (~f ~@(take (count colls) infinite-symbol-list))))</div></div><div><br></div><div>I change colls to `~colls inside the interleave:</div><div><br></div><div><div>(defmacro combins</div><div> [f & colls]</div>
<div> `(for ~(vec (interleave infinite-symbol-list `~colls))</div><div> (~f ~@(take (count colls) infinite-symbol-list))))</div></div><div><br></div><div><div>(combins try-args [+ -] (keys arg-types))</div></div><div>
<div>>> (nil nil nil (#<core$_PLUS_ clojure.core$_PLUS_@507726> :number) nil nil nil nil nil nil (#<core$_ clojure.core$_@ac9cbe> :number) nil nil nil)</div></div><div><br></div><div>It works! Can anybody explain why that works? I just now tried this thinking it might solve the problem where it seemed to be evaluating the terms in colls. I figured that back-quoting the return value might stop the return value from further evaluation. But I have no idea why this solves the problem of it returning nils where it should not have been--I still don't even know why it was doing that incorrectly in the first place.</div>
<div><br></div><div>-Omri</div>