Clojure funcs don't seem to work properly when sent through a list

In Clojure REPL, this expression

( #(for [x %] (+ 100 (second x))) ['(+ 38) '(+ 48)] ) 

produces (138,148) as expected

but this one

 ( #(for [x %] ((first x) 100 (second x))) ['(+ 38) '(+ 48)] ) 

produces (38 48), which seems really strange.

Both expressions really should give the same result! What am I missing? Any ideas for resolving this mystery will fall in love.

By the way, I tried to use "apply (first x)" and pack the rest of the arguments into a list, but that doesn't seem to matter. The same unexpected result is returned.

Also, to make sure "+" is really allowed by input, I gave REPL

following:
 ( #(for [x %] (resolve (first x) )) '((+ 38) (+ 48)) ) 

which produced

  (#'clojure.core/+ #'clojure.core/+) as expected. 
+4
source share
2 answers
 ( #(for [x %] ((first x) 100 (second x))) ['(+ 38) '(+ 48)] ) 

In this, + is a symbol, not a function, because it was listed. However, characters are defined as performing a map search when a function is called (similar to keywords). So, ('+ 100 38) same as (get 100 '+ 38) . This last argument is "if you cannot find what I want on the map, return it." Since 100 not a mapping, + uses this argument as the return value.

To do what you want, you have two options:

  • Using vectors instead of quoted lists ensures that + will be resolved appropriately.

     ( #(for [x %] ((first x) 100 (second x))) [[+ 38] [+ 48]] ) 
  • Observe it to make sure you use the + function instead of the + symbol.

     ( #(for [x %] ((resolve (first x)) 100 (second x))) ['(+ 38) '(+ 48)] ) 
+10
source

When quoting a list, for example '(+ 38) , none of the elements in the list are evaluated. Thus, + is just a symbol, not a reference to the addition function from clojure.core.

The result of calling this character as a function is a bit confusing, especially since you call it with two arguments. The reason was already explained by @mange: calling the character as a function tries to find the character in the first argument, returning the second (optional) second argument by default when the search fails:

 ('x) ; throws ArityException ('x 1) ;=> nil ('x 1 2) ;=> 2 ('x 1 2 3) ; throws ArityException 

You have several options:

  • Use vectors instead of quoted lists: [+ 38] . All elements of the vector are evaluated (as in an unquoted list), but the vector is only a data structure, not a syntax for calling a function, like a list.
  • Use the resolve function to find the function referenced by the character. Notice that resolve looks at the character in the current namespace. Therefore, if building a quoted list and calling a function occurs in different namespaces, this can lead to unexpected results (if you have two different definitions for the same character in different namespaces).
  • Use syntactic quotation instead of simply quoting in the list and do not specify a character (evaluating it): `(~+ 38)
+3
source

Source: https://habr.com/ru/post/1386994/


All Articles