At the Clojure REPL, this expression
( #(for [x %] (+ 100 (second x))) ['(+ 38) '(+ 48)] )
produces (138 148) as expected
but this
( #(for [x %] ((first x) 100 (second x))) ['(+ 38) '(+ 48)] )
produces (38 48) which seems truly weird.
Both expressions really should be producing the same result! What am I missing? Will appreciate any ideas to resolve this mystery.
BTW, I tried to use 'apply (first x)' and package the rest of the args into a list but it doesn't seem to matter. The same unexpected result comes back.
Also, to verify that the + indeed gets resolved from the input, I gave the following to the REPL
( #(for [x %] (resolve (first x) )) '((+ 38) (+ 48)) )
which produced
(#'clojure.core/+ #'clojure.core/+) as expected.
( #(for [x %] ((first x) 100 (second x))) ['(+ 38) '(+ 48)] )
In this the +
is a symbol, not a function, because it has been quoted in the list. However, symbols are defined as doing map lookup when invoked as a function (the same as keywords). So ('+ 100 38)
is the same as (get 100 '+ 38)
. That last argument is the "if you can't find what I want in the map, return that". Since 100
is not a map, +
uses that argument as the return value.
To make it do what you want you have two options:
Use vectors instead of quoted lists ensures that +
gets resolved appropriately.
( #(for [x %] ((first x) 100 (second x))) [[+ 38] [+ 48]] )
Resolve it yourself to ensure that you use the +
function instead of the +
symbol.
( #(for [x %] ((resolve (first x)) 100 (second x))) ['(+ 38) '(+ 48)] )
When you quote a list, like '(+ 38)
, none of the items in the list are evaluated. Thus the +
is just a symbol and not a reference to the addition function from clojure.core.
The result of calling this symbol as a function is a bit confusing, especially since you happen to call it with exactly two arguments. The reason was already explained by @mange: Invoking a symbol as a function tries to look up the symbol in the first argument, returning the (optional) second argument as a default when the lookup fails:
('x) ; throws ArityException
('x 1) ;=> nil
('x 1 2) ;=> 2
('x 1 2 3) ; throws ArityException
You have several options:
[+ 38]
. All elements of a vector are evaluated (as in an unquoted list), but the vector is just a data structure, not the syntax for function invocation as is the list.resolve
function to find the function referenced by the symbol. Note that resolve
looks up the symbol in the current namespace. So if the construction of the quoted list and the invocation of the function happen in different namespaces, this may lead to surprising results (if you happen to have two different definitions for the same symbol in the different namespaces).`(~+ 38)
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