Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Why does the pure function #1@#2& have no name? Is it due to the way Mathematica works, or is it "just because"?

I'm asking because I find inelegant to write expressions like

MapThread[#@#2 &, {{a, b, c}, {1, 2, 3}}]
{a[1], b[2], c[3]}

I would rather have a name form such as

MapThread[Work, {{a, b, c}, {1, 2, 3}}] 

Work is not the best choice but I'm open on suggestions :)

  • One may say that there is no name because #1@#2& is short enough. But f/@{1,2,3} is shorter than Map[f,{1,2,3}] and Map exists :)
  • Then I've thought, maybe it is because Map gives other possibilities with 3rd argument and this is the difference. Well, it is a difference, but so what, why ther is no Work?
share|improve this question
5  
There is, but it is deprecated: Compose. You can also use Composition[#1][#2] &, although this is hardly better. I still use Compose myself, but I would not take the responsibility to recommend this as a common practice. –  Leonid Shifrin Jul 18 '13 at 12:11
    
@LeonidShifrin Oh, it is indeed. I haven't thought because I thought that it works like Composition and Composition does not work like I wanted –  Kuba Jul 18 '13 at 12:13
1  
@LeonidShifrin I think You should post this as an answer since documentations says "Compose has been superseded by Composition" only. What could be misleading –  Kuba Jul 18 '13 at 12:15
3  
Just so novices won't be confused here, I'd like to add that nothings preventing anyone from just defining Work=#1@#2& or similar. Also in your example you can use MapAll in place of Work, but that's just a happy coincidence and will break as soon as you do anything slightly different. :P –  jVincent Jul 18 '13 at 12:21
    
@Kuba Ok, done. –  Leonid Shifrin Jul 18 '13 at 12:23

2 Answers 2

up vote 31 down vote accepted

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.

share|improve this answer
    
This is the first case when I do not see any reason to wait with accept :) But I will to be fair :) Thank You. –  Kuba Jul 18 '13 at 12:26
    
@Kuba No problem :). This question was destined to emerge sooner or later. –  Leonid Shifrin Jul 18 '13 at 12:28
    
@LeonidShifrin chapeau again and again (+1)...i have still one question, if we say that #@#2& is short for Function[#1[#2]] then i don't see why: MapThread[Function, {{a,b,c},{1,2,3}}] shouldn't work...the intention is clear. –  Stefan Jul 18 '13 at 12:37
1  
@Stefan ... Therefore, using Function in MapThread will be no different from any other head, which is what you 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. –  Leonid Shifrin Jul 18 '13 at 12:50
1  
@Stefan Yes, Function is used to delay evaluation, basically as a run-time macro with delayed evaluation, but not to construct expressions from pieces. I guess this is a Mathematica - specific issue and may be unintuitive when viewed from viewpoints of some other languages. In a sense, there are no functions in Mathematica, just rules and evaluation procedure. –  Leonid Shifrin Jul 18 '13 at 13:07

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.

share|improve this answer
    
This is correct, but does not seem very relevant to the question being discussed. The question seems to be about a very particular construction, and my digression into some particular aspects of pure functions in my answer was motivated by the comments rather than the original question itself. If your answer is a kind of a reply to that part of my answer, then I hasten to comment that I did not intend to describe pure functions in any extensive manner there, but only considered one particular aspect of them, relevant to the original question and answering the questions raised in comments. –  Leonid Shifrin Jul 18 '13 at 18:17
    
Leonid thanks for your comment. I'm new on SE and I'm learning how it works. I thought your answer, while correct, was not fully centered on the question. Indeed, the question I tried to answer is "Why there is no name for #1@#2&". The use of Compose/Composition is something about a possible alternative to use the pure function, not explaining why pure functions don't use variable's name. At least, this is my interpretation of the question. If I misinterpreted the question and you think my answer is not relevant for the whole thread, I can remove my answer. –  bobknight Jul 19 '13 at 6:45
    
@bobknight You have convinced me that it is possible to interprete my question this way. However, as Leonid Shifrin has said, it was about very particular construction, that's why I haven't precised it better. Maybe I should edit the title to: "Is there a built-in function identical to pure #@#2&?" ? –  Kuba Jul 19 '13 at 8:16

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.