Reap has the attribute HoldAll. This is necessary so that its argument is not evaluated until Reap has set up the conditions necessary to capture the values designated by Sow. The implementation of @* (Composition) is such that it interferes with the attributes of the right-most composed function. In the exhibited example, the Sow expression is evaluated prematurely because Composition interferes with the HoldAll attribute on Reap.
Analysis
This behaviour will effect any function with attributes, not just Reap. The following examples show how Composition effectively strips the HoldAll attribute off of Hold...
f @ Hold[1 + 1]
(* f[Hold[1 + 1]] *)
(f @* Hold)[1 + 1]
(* f[Hold[2]] *)
Composition[f, Hold][1 + 1]
(* f[Hold[2]] *)
... and here is an example showing SequenceHold being ignored:
SetAttributes[ss, SequenceHold]
Defer @ ss[Sequence[1, 2, 3]]
(* ss[Sequence[1, 2, 3]] *)
(Defer @* ss)[Sequence[1, 2, 3]]
(* ss[1, 2, 3] *)
The problem is that f @* Hold is behaving as if it were implemented as f @ Hold[##] &:
f @ Hold[1 + 1]
(* f[Hold[1 + 1]] *)
Composition[f, Hold][1 + 1]
(* f[Hold[2]] *)
(f @ Hold[##] &)[1 + 1]
(* f[Hold[2]] *)
The expression f @ Hold[##] & is a pure function that has no attributes. In particular, it does not have the HoldAll attribute. Thus, it evaluates its argument before passing it on to the body of the function. Just like Composition. And, also like Composition, functions such as these preserve the attributes of all functions but the right-most:
ClearAll[ff, gg]
SetAttributes[{ff, gg}, HoldAll]
ff[x_] := Defer[22 + x]
gg[x_] := 11 + x
ff @ gg[1 + 1]
(* 22 + gg[1 + 1] *)
Composition[ff, gg][1 + 1]
(* 22 + gg[2] *)
(ff @ gg[##] &)[1 + 1]
(* 22 + gg[2] *)
On an incidental note, degenerate cases involving Composition actually do preserve function attributes. Consider:
Composition[Hold][1 + 1]
(* Hold[1 + 1] *)
Composition[Identity, Hold][1 + 1]
(* Hold[1 + 1] *)
Indeed, optimizations involving Identity sometimes go too far, getting the wrong answer:
Hold @ Identity[1 + 1]
(* Hold[Identity[1 + 1]] *)
Composition[Hold, Identity][1 + 1]
(* Hold[1 + 1] *)
But we digress. To work around the main problems under discussion, we can implement our own composition using a Function with appropriate attributes. For example, we can preserve HoldAll like this:
Function[Null, f @ Hold[##], HoldAll][1 + 1]
(* f[Hold[1 + 1]] *)
General Workaround
We could also define our own general composition operator that makes a pretty good attempt at preserving attributes:
comp[f___] :=
Module[{h}
, SetAttributes[h, HoldAll]
; ReplacePart[
Function[Null, Null, HoldAllComplete]
, h[f, ##] //. h[a___, b_, c_] :> h[a, b@c]
, 2
, 1
]
]
This attempt is not fool-proof, but in practice it is usually good enough (and better than Composition):
comp[f, Hold]
(* Function[Null, f[Hold[##1]], HoldAllComplete] *)
comp[f, Hold][1 + 1]
(* f[Hold[1 + 1]] *)
comp[Defer, ss][Sequence[1, 2, 3]]
(* ss[Sequence[1, 2, 3]] *)
comp[ff, gg][1 + 1]
(* 22 + gg[1 + 1] *)
comp[Hold][1 + 1]
(* Hold[1 + 1] *)
comp[Identity, Hold][1 + 1]
(* Hold[1 + 1] *)
comp[Hold, Identity][1 + 1]
(* Hold[Identity[1 + 1]] *)
It would be better if Composition were built into the Mathematica kernel in such fashion as to respect attributes. However, that would represent a breaking change to a long-standing language feature.
Compositionis discussed at length in (54762). – WReach Apr 16 '15 at 17:55