Clojure: returning a vector from an anonymous function
Asked Answered
O

4

25

I wrote a small anonymous function to be used with a map call. The function returns a vector containing a column name and column value from a SQL result set query.

Here is the function (input is the column name):

(fn [name] [(keyword name) (.getObject resultset name)])

This works fine, however when I tried to use a "simplified" version of the anonymous function, I got an error:

#([(keyword %) (.getObject resultset %)])

java.lang.IllegalArgumentException: Wrong number of args (0) passed to: PersistentVector

Here is the map call:

(into {} (map (fn [name] [(keyword name) (.getObject resultset name)]) column-names))

Is it possible to use the simplified syntax for this function? If so, how?

Thanks.

Ostrogoth answered 7/2, 2011 at 12:57 Comment(1)
Some more information: groups.google.com/group/clojure/browse_thread/thread/…Ratchford
I
29

Your problem is that the simple syntax is trying to evaluate the vector as a function call.

You can insert an "identity" function to make it work, as this is just a simple function that will return the vector unchanged:

#(identity [(keyword %) (.getObject resultset %)])
Ironwork answered 7/2, 2011 at 13:3 Comment(4)
I chose this answer because it also explains why the problem occurs, although the answer by Alex Ott also works (and provides a general solution for other data structures). Thanks, all.Ostrogoth
I edited this answer to incorporate the Alex's suggestion, but it looks like it was ignored for some reason. Oh well.Bratton
I added an example of the more ideal solution rather than just showing code that demonstrates why it wasn't working. Since you edit didn't take.Ordinarily
Using #(do [(keyword %) (.getObject resultset %)]) is shorter and probably a little faster than using identityRodrigo
N
27

You need to use vector function to do this:

#(vector (keyword %) (.getObject resultset %))

P.S. there are also functions for maps, sets, etc.

Neufer answered 7/2, 2011 at 13:11 Comment(0)
D
5

Yeah, Clojure should really support a #[...] construct, just for this case.

I would recommend the following as the best alternative:

#(vector (keyword %) (.getObject resultset %))
Dammar answered 7/2, 2011 at 13:11 Comment(3)
I don't think #[...] would be common enough to warrant special syntax for it, and once you learn about it, throwing vector in there isn't a big deal.Bratton
I thought of the same #[...] construct. How could I write a macro for it? # means reader macro right?Kandace
You can write #(-> [(keyword %) (.getObject resultset %)]), but I find this unclearKandace
M
0

There are already good answers. But no answer explains what is going on.

In Clojure you can define anonymous function literals with #(...). Nothing else can be used to define anonymous function literals. This happens before the form reaches the evaluation. This is a so called reader macro. The reader really must see this #(. When seeing it, the reader immediatelly replaces it by something like (fn [args] (...). This is explicitly defined here.

That means, you can only ever use anonymous function literals for things, that have exactly this format with the parenthesis. If all you want to do is returning something without calling anything, anonymous function literals do not work. The following examples cannt be written with anonymous function literals, because they need no parenthesis.

(fn [x] x)
(fn [x] [x])
(fn [] 7)
(fn [] [7])

As you can see in the other answers there are good workarounds, that enable you to write something, that needs the parenthesis.

On the one hand it would be interesting to write a user macro #[...]. But on the other hand, as you saw, in your case this is not really needed. There are good solutions for your question. See also this question about the topic.

Note that when working with EDN data, there is principally a solution. But most likely this does not answer your question, because you probably don't want to use the EDN reader.

Magnesium answered 24/3 at 10:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.