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.