33

What is the difference between

f@*g@*h@x

and

f@g@h@x

Both evaluate to

f[g[h[x]]]

If they're the same, why introduce Composition as a new feature?

orome
  • 12,819
  • 3
  • 52
  • 100

2 Answers2

35

Clearly the @ notation is inspired by the usual mathematical notation for function composition. f@g[x] looks very similar to the mathematical notation $(f\circ g)(x)$. But it is important to understand that @ does not denote function composition. In mathematical notation $f\circ g$ is also a function. In Mathematica f@x is simply a different way to write f[x], but f@g is not (generally) a function. Both f@x and f[x] parse to the exact same Mathematica expression.

So what is the true equivalent of $f \circ g$? It is Composition[f, g] which can be more concisely written as f @* g since version 10, and can be used in situations where we need a function without applying it to an argument (e.g. with Map, or as an operator with Dataset).


Both of your examples evaluate to the very same things in the end, so they behave equivalently in this case. But the way Mathematica arrives to the same end result is different:

f@g@h@x parses to an expression with the FullForm f[g[h[x]]], which doesn't evaluate further. There's no evaluation step.

f@*g@*h@x parses to an expression with the full form Composition[f,g,h][x], which then evaluates to f[g[h[x]]].


It's also worth pointing out that @ and @* have different precedences and associativity properties as operators. f@g@x is equivalent to f@(g@x) and f@*g@x is equivalent to (f@*g)@x. Writing ...[...] has a different precedence again so f@*g[x] is the same as f@*(g[x]) (i.e. it's not the same as f@*g@x).

orome
  • 12,819
  • 3
  • 52
  • 100
Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • Could you say a bit more about the different meanings? – orome Apr 03 '15 at 15:24
  • I think it is that last construct, Composition[f,g], that is the most useful as you can easily pass around the composed function without having to craft it by hand. – rcollyer Apr 03 '15 at 15:25
  • 4
    Just to make it more clear, the big difference is that f @ g is the same as f[g], while f @* g is roughly equivalent to f[g[#]] &. For example of usage, StringLength @* ToString /@ Range[10] works whereas StringLength @ ToString /@ Range[10] does not. – 2012rcampion Apr 03 '15 at 15:25
  • @2012rcampion I would be cautious with the use of "equivalent". f @* g is not the same as f[g[#]]&. It is equivalent to Composition[f,g]. In the sense that either f @* g or Composition[f,g] are interpreted as the very same expression during parsing. It happens to be that f[g[#]] & works the same way as Composition[f,g], but they are very different expressions. (And of course, f[g[#]] & doesn't work exactly the same way, there are several subtle details that show in special edge cases. Plus Composition used to unpack packed arrays pre-version-10...) – Szabolcs Apr 03 '15 at 15:28
  • 1
    That's why I added "roughly equivalent." and changed the first "equivalent" to "is the same," to make clear the differences. But if you don't know the details about Composition it gets the point of what it does across. – 2012rcampion Apr 03 '15 at 15:30
  • @2012rcampion Sorry, somehow I didn't see "roughly" ... – Szabolcs Apr 03 '15 at 15:30
  • @Szabolcs It was a late edit... for some reason I keep waiting until right before the five-minute deadline to proofread my comments – 2012rcampion Apr 03 '15 at 15:32
  • I'm not as smart as I pretend to be, and I don't quite get the difference. So it would help me if it could be spelled out a bit. I see that they are different, but could you go into a bit more detail about exactly hat differs? – orome Apr 03 '15 at 15:34
  • @raxacoricofallapatorius Do you understand what parsing means, and in the context of Mathematica do you understand the difference between parsing and evaluation? – Szabolcs Apr 03 '15 at 15:38
  • @Szabolcs: Yes (enough, anyway). Maybe a more side-by-side presentation of the differences would do it for me. – orome Apr 03 '15 at 15:41
  • @raxacoricofallapatorius So is it clear then how f@*g@x and f@g@x simply parse to different expressions, which then are evaluated to the same? Try wrapping both in FullForm@Hold[...] to prevent evaluation and see them written using the same notation. – Szabolcs Apr 03 '15 at 15:47
  • @Szabolcs: Yes. I guess how they differ in some practical examples might be illuminating. – orome Apr 03 '15 at 17:06
  • @raxacoricofallapatorius They do work the same way most of the time. Feel free to use either. @* is more useful e.g. here: Map[f@*g, ..., {2}] or in any situation where you need a function but do not need to apply it to anything. There's also the fact that (f@*g)[x] requires parentheses unlike f@g[x] or f@*g@x, so in the situation you describe f@*g seems more cumbersome and error prone when modifying code ... – Szabolcs Apr 03 '15 at 17:11
  • 1
    @Szabolcs: So is it fair to say that the (or at least a) key conceptual distinction is that @* creates a function (which is not what @ does)? – orome Apr 03 '15 at 17:14
  • 2
    @raxacoricofallapatorius Yes. That is correct. – Szabolcs Apr 03 '15 at 17:14
  • @Szabolcs: It's funny (and I expect surprising, and maybe a bit frustrating to people who really understand something) what simple things can unlock understanding. Could you add something explicit abut "creating a function" in contrast to what @ does to your answer (maybe with an example)? It would make a great answer even better. Thx. – orome Apr 03 '15 at 17:18
  • @raxacoricofallapatorius You're right, I wasn't very good at explaining this ... that's why I resorted to asking questions in the comments instead. – Szabolcs Apr 03 '15 at 17:20
  • @Szabolcs: It's a great explanation — I was just missing something pretty basic. – orome Apr 03 '15 at 17:22
  • Really great answer (and greatly improved). Thanks. A follow up: ...[...] and ...@... have different precedence? – orome Apr 03 '15 at 22:48
  • Any clue as to why @* was chosen over and why \[SmallCircle] is not even available as an undefined operator? – Alan May 02 '18 at 20:46
20

... why introduce Composition as a new feature?

Composition is used to create a new anonymous function that can be used in all the standard ways such as Map and Apply etc. To achieve the same thing without it one needs a Function. Much like operator forms the use of Composition allows one to eliminate extraneous Function constructs which can make code more linear and easier to read. The short form @* and its companion /* (see RightComposition) make its application pleasingly concise.

Composition is not typically used for a single application but instead when one wishes to perform an operation multiple times. Here are some related form:

f@*g@*h /@ {x, y, z}
f@g@h@# & /@ {x, y, z}
f /@ g /@ h /@ {x, y, z}

Each of these evaluate the same way in this particular case but there are differences that are not immediately apparent. We can see some of them if we define:

f = HoldForm; g = Print; h = Sqrt;

Now:

f@*g@*h /@ {x, y, z}
f@g@h@# & /@ {x, y, z}
f /@ g /@ h /@ {x, y, z}

enter image description here

Because Composition does not hold its arguments the Symbols g and h are resolved to Print and Sqrt in the output from the first line. In the second line f resolves first to HoldForm and as a result g and h remain verbatim. Finally in the last line each function application is evaluated sequentially therefore the Print fires and Null is returned as the actual output.

See also:

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371