Is there a name for #1@#2&?
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.
Its Name Is Construct
The built-in function Construct
has been introduced in v11.3. The original question was asked and answered in 2013, prior to the release of v11.3 and the introduction of Construct
.
Construct[f, x]
f[x]
Construct[f, x, y, z]
f[x, y, z]
Construct
does precisely what question seems to be looking for:
MapThread[Construct, {{a, b, c}, {1, 2, 3}}]
{a[1], b[2], c[3]}
I argue that neither Compose
nor Composition
is the correct answer to the question as asked. Note that while Composition
can sometimes replace Construct
in specific circumstances (like the last example), Composition
composes functions resulting in a new function that can then be applied to arguments, while Construct
actually applies the function to the argument(s).
Composition[f, g]
f@*g
Construct[f, g][x]
f[g][x]
Unfortunate Naming Conventions
The name of Construct
is surely meant to evoke the cons
function from Lisp which takes two arguments and constructs a list with the first argument as the head (car
) and the second argument as the tail (cdr
). (A "constructor" in object-oriented languages also shares this etymology.) The named function fills a gap in Wolfram Language: Prior to Construct
the square brackets notation was nameless.
On the other hand, the "names" for the other function application operators do not correspond to the functions that define them. For example, @
is named Prefix (by Precedence
, for example), but the Prefix
function merely affects the display of a function that is already applied. Likewise, ~
is named Infix, and //
is named Postfix, but neither corresponds to the functions of the same name. Maybe they should be named PrefixConstruct, InfixConstruct, and PostfixConstruct. Alas, they didn't consult me before naming them.
Another important role played by pure functions (those built with slots #) is that in functional programming (and Mathematica follows that paradigm) many times you need to apply a function that doesn't live outside the specific place where you define/evaluate it. In other words, mapping a function to a data set can be done without to save any unneeded variable/function in memory, just using a pure function.
Names["Global`*"]
{}
MapThread[#@#2 &, {{a, b, c}, {1, 2, 3}}]
{a[1], b[2], c[3]}
Names["Global`*"]
{"a", "b", "c"}
f[letter_, number_] := letter[number];
MapThread[f[#1, #2] &, {{a, b, c}, {1, 2, 3}}]
{a[1], b[2], c[3]}
same result but now you have many more variable in the memory and they are not needed outside the MapThread itseldf, so the memory used is waste.
Names["Global`*"]
{"a", "b", "c", "f", "letter", "number"}
For that using pure the variables have no names.