Compose
and Composition
There is, but it is deprecated (in favor of Composition
): Compose
:
MapThread[Compose, {{a, b, c}, {1, 2, 3}}]
(* {a[1], b[2], c[3]} *)
I still use Compose
myself, but I would not take the responsibility to recommend this as a common practice. You can also use Composition[#1][#2] &
, although this is hardly better than your original suggestion in terms of code brevity.
Composition
is more general because the result of Composition
is a function which can take several arguments:
Composition[f, h][x, y]
(* f[h[x, y]] *)
Composition
also has a Flat
attribute, which is a mixed blessing (see e.g. discussion in this answer, where this leads to a huge slow-down for iteratively constructed composition of functions).
Compose
and Composition
vs. Function
- a clarification
Finally, there was a question in the comments why one can not use Function
in a similar manner, since one may get an impression that Function
is also constructing function calls. Actually, this is not quite so.
Compose
is used basically to construct the square brackets, which is syntactically (and also semantically) non-trivial operation. One doesn't have to tie that to functions - one can think of Compose
as a tool for programmatic building of normal expressions with non-trivial heads (the same is also true for Composition
). So, where we would type something like
f[a]
or
f @ a
we can now do that programmatically as
Compose[f, a]
or similarly with Composition
. This is a non-trivial capability, and it has to do with our ability to programmatically construct normal expressions from symbols / other normal expressions. For example, consider the following expression:
expr = Sin[x + Cos[y*z]];
We can get the symbols it is built with:
syms = Cases[expr, _Symbol, Infinity, Heads -> True]
(* {Sin, Plus, x, Cos, Times, y, z} *)
Here is how one can reconstruct it from symbols, using only Composition[..][..]
:
Composition[Sin, Plus][x, Composition[Cos, Times][y, z]]
(* Sin[x + Cos[y z]] *)
Of course, built-in Composition
itself is not that magical, and one can write their own version of Composition
using e.g. replacement rules (and the same is true for Compose
). But it is important to recognize their conceptual significance as functions which encapsulate programmatic expression-building. And they could not care less whether expressions they build are executable code (evaluate non-trivially), or just inert symbolic trees.
Now, Function
serves a different purpose - it allows to construct function calls programmatically by generating a function call code from a function (basically a macro with placeholders) and a sequence of arguments at run-time. When we define a function like
plus = Function[#1 + #2]
we in fact define a macro which substitutes the parameters of the actual function call like
plus[1, 2]
(* 3 *)
into the body #1+#2
and only then evaluates the body.
So Function
has to use lazy evaluation, to allow us to separate the process of defining a function expression, from calling that function with some arguments. And, in terms of execution time, it allows one to postpone the evaluation from "definition-time" to run-time (thus Function
is HoldAll
).
This is a different purpose from that of Compose
and Composition
(which, for example, don't carry Hold*
-attributes, because they don't have to prevent any evaluation). By itself, Function
is not able to syntactically construct an expression from its head and elements. And MapThread[f,{{a,b},{x,y}}]
will return {f[a,x],f[b,y]}
for a generic f
. Therefore, using Function
in MapThread
will be no different from any other head, which is what one can observe when substituting Function into MapThread
in the original example. Put in other way, Function
takes care of slots and the ampersand in #1@#2&
, but not of @
, which is the important thing here.
Compose
. You can also useComposition[#1][#2] &
, although this is hardly better. I still useCompose
myself, but I would not take the responsibility to recommend this as a common practice. $\endgroup$Composition
andComposition
does not work like I wanted $\endgroup$Work=#1@#2&
or similar. Also in your example you can useMapAll
in place ofWork
, but that's just a happy coincidence and will break as soon as you do anything slightly different. :P $\endgroup$