Why doesn't a Composition[] hold its arguments and what can be done about it?

Now that Composition (also RightComposition) has shorthand syntax I am more inclined to use it, however what I feel should be one of its best advantages over the plain foo @ bar @ ## & doesn't exist: held arguments. For example I wish for the following behavior:

Composition[ToString, HoldForm][2 + 2]

(* desired output: "2 + 2" *)


I am aware that the form head[a__][b__] doesn't normally allow holding of arguments b but this has always seemed like a place an exception should have been made. Unless the first head applied has hold Attributes this would not alter behavior. Does anyone regularly make use of the evaluation of arguments that I am seeing as undesirable?

Presumptuously assuming that others find desirable the behavior that I want I see this as a sorely missed opportunity:

• the shorthand forms foo @* bar and foo /* bar could have been made to parse as LeftComposition (hypothetical) and RightComposition
• these new operators could have been made to hold arguments as I describe without breaking backwards-compatibility with Composition in past versions.

I wonder what can now be done to improve the situation.

1. I suppose it is too late to get such changes made to natively. Has such a change ever been made to Mathematica (or Wolfram Langauge) after an initial release?

2. I am tempted to try to force this behavior myself using one of the methods available. All public code except what I see as edge cases would work on my machine but my own code would fail on others.

3. Sadly it is not possible to define custom compound operators so there is no true alternative, but I could define e.g. comp[fn1, fn2] to work as I desire.

My questions therefore:

Is there some reason not to have a Composition expression hold its arguments?

Is there another option that I am failing to consider?

How can one robustly implement option #2 or #3?

-
When I think about some use-cases, I guess evaluating the arguments is more natural, because in f @* g @* h @ x it is not very likely that f, g or h hold their arguments anyway. On the other hand, people will maybe use the evaluation, where x is a variable containing a larger expression which needs to be evaluated. As for your option #1: I'm only aware of the Locked attribute of Locked, where a thing was changed which was already for very long in the core system. –  halirutan Jul 14 '14 at 3:15
As for your example: using the shorthand syntax, you need braces around (2+2) anyway. Isn't it easier to just write ToString@*HoldForm@Unevaluated[2 + 2] than bending the system? Or maybe ToString@*HoldForm@*Plus @@ {2, 2} in this specific case. I guess reimplementing Composition is not a good idea. Although it works here, in general Composition can be used with e.g. InverseFunction which breaks this approach. –  halirutan Jul 14 '14 at 3:19
@halirutan Could you provide a concrete example where the holding would break the code? I can certainly envision one, which I'll post soon, but I'd like to see others. -- Or I could just wait for you to finish your comments. :^) –  Mr.Wizard Jul 14 '14 at 3:20
@Mr.Wizard : Slight off topic question (may be too minor to ask separately): Why the form f @* g @ x is preferred over f @ g @ x? I can understand that we may need to define h = f @* g and later use h @ x. Is there any other reasons? –  Yi Wang Jul 14 '14 at 8:38
Generally, b can't be held in f[a][b]. It's a property of how evaluation works and to change it would require a significant change to the evaluator, as well as thinking through all the consequences. I could think of several interesting applications other than Composition if b could be held, e.g. it would be useful with Inactive. –  Szabolcs Jul 15 '14 at 0:54

I'm the one inside the company who suggested RightComposition (and pushed for syntax for Composition and RightComposition).

I'm sympathetic to your need, and have wanted the same thing once or twice myself. Given that not much /* and @* code has been written yet, I think it is certainly possible we could have /* parse to LeftComposition. I'm not sure what kernel shenanigans would be necessary for LeftComposition and RightComposition to hold their sub-values, however. It may simple be impossible without big changes to the evaluator, performance regressions, etc.

And even if possible, we have much higher priority kernel changes that need to be done, and of course finite resources. Some examples that happen to be fresh in my mind:

1. supporting L-value assignment for arbitrary symbols is not possible via up-values. We need this to make it possible to implemenet Dataset[...][[All, 1]] = ... and CloudSymbol["foo"][[1]] = ... in top level, without custom C code.
2. FullDefinition is currently broken in a subtle but important way, in which it can put definitions in the wrong order.
3. We also need pattern matching to work on associations (e.g. <|"A" -> _|>, which doesn't currently work).
4. We need ReplaceAll to go inside associations, which currently doesn't work.
5. Many functions still don't work on associations (e.g. RandomSample).

These are high priority problems. So I wouldn't hold your breath for any changes to /* and @*

-
Thanks a lot for taking the time to answer my question. I realize that as outlined there are other priorities, and though I am pleasantly surprised that there is a slim chance for the change I proposed I indeed won't hold my breath. However, since you were a motivator for syntax extensions are you open to discussing some other ideas? –  Mr.Wizard Jul 14 '14 at 6:21
By the way, if you have time please look at (54721) -- the new system is quite opaque to me, and I think this could be of broad interest. –  Mr.Wizard Jul 14 '14 at 7:04

Using this site as my rubber duck and attempting to answer my own questions:

(1) Reason for existing behavior

One may want to be able to do this:

heldRow = HoldForm @* Row @* List;   (* version 10 syntax *)

x = 7;

Block[{x},
heldRow[x + x + x, x^2*x^3]
]

3 xx^5        (*  proposed behavior would yield:  x+x+xx^2 x^3  *)


My counterargument: this seems fairly uncommon and if needed it is easily accomplished with Function:

heldRow = HoldForm @ Row @ List @ ## &;


(3) Implementation

For a separate function we might use this:

comp[fns__] := Function[, Composition[fns] @@ Unevaluated /@ Hold[##], HoldAll]


This can be prettified with a formatting rule such as:

MakeBoxes[
p : Function[, c_Composition @@ Unevaluated /@ Hold[##], HoldAll],
_
] := InterpretationBox[RowBox[{"|", #, "|"}], p] & @ ToBoxes[c]


Now:

comp[HoldComplete, Row, List]

| HoldComplete@*Row@*List |


I am now playing with modifications to the System functions.

-
I haven't given this any thought, but I'd like to point out a couple of feelings. 1) I value (don't ask why) that compositions remain unevaluated if they are not applied to something, instead of turning into a Function. 2) When I read your question, I thought the natural way it should work was a different one (don't ask why, and I DONT THINK it would be beneficial. I just assumed it more natural). Namely, that doing (f@*g)[x] would be like inputting x to g, and then giving the output to f. So for ex, if h holds, (f@*h)[2+2] would give f[h[2+2]], but (h@*f)[2+2] would give h[f[4]]. –  Rojo Jul 14 '14 at 17:00
@Rojo The only way I know to keep comp[. . .] unevaluated is to make use of Leonid's Stack[_]` method in the linked answer; I'll post an implementation of that when I have time. As to your natural way: that is actually what I was thinking when I write the line that is now struck out in the Question, but then thought "no, that's not right." Perhaps that functionality should also be considered or explored? –  Mr.Wizard Jul 14 '14 at 23:41