Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Standard version or idiomatic use of (fn [f & args] (apply f args))

Tags:

clojure

Every so often I find myself wanting to apply a collection of functions on several collections of parameters. It's easy to do with map and a very simple function.

(map
  (fn invoke [f & args] (apply f args))
  [- + *]
  [1 2 3]
  [1 2 3]
  [1 2 3])

 (-1 6 27)

Searching the web turns up quite a few libraries that define a similar function, often called funcall or invoke. Because of Clojure's penchant for variadic functions, I cannot help but think there should already be a default version of this function.

Is there, or is there another idiomatic way to solve situations like this ?

Edit:

Another form may be

(map
  (comp eval list)
  [- + *]
  [1 2 3]
  [1 2 3]
  [1 2 3])

 (-1 6 27)

Which scares me because of the eval.

like image 488
NielsK Avatar asked Nov 18 '11 13:11

NielsK


2 Answers

Edit: this will do what you want (as @BrandonH mentioned):

(map #(apply %1 %&) [- + *] [1 2 3] [1 2 3] [1 2 3])

But this is hardly an improvement over your version -- it just uses a shorthand for anonymous functions.


My understanding is that FUNCALL is necessary in Common Lisp, as it's a Lisp-2, whereas Clojure is a Lisp-1.

like image 28
Matt Fenwick Avatar answered Oct 10 '22 20:10

Matt Fenwick


If you really don't have a clue about the function name, but you know what the in- and output have to be, you can try https://github.com/Raynes/findfn.

(find-arg [-1 6 27] map '% [- + *] [1 2 3] [1 2 3] [1 2 3])
;=> (clojure.core/trampoline)

This tells us that

(map trampoline [- + *] [1 2 3] [1 2 3] [1 2 3])
;=> (-1 6 27)

Actually, you can abuse trampoline as funcall in clojure. But it is hardly idiomatic, because it is a Lisp-1. The above code evaluates to:

[(trampoline - 1 1 1), (trampoline + 2 2 2), (trampoline * 3 3 3)] which then becomes [-1 6 27] (in the form a of lazyseq to be precise).

As Adrian Mouat points out in the comment below, this probably isn't the preferred way to solve it. Using a funcall like construct smells a bit funny. There must be a cleaner solution. Until you've found that one, findfn can be helpful ;-).

like image 185
Michiel Borkent Avatar answered Oct 10 '22 19:10

Michiel Borkent