# Making a rule that only applies when the expression is NOT an argument?

I know we can make rules of the kind:

magic/:f_[x___,magic[a_],y___]:=f[x,arg[a],y]


This will replace magic[a] by arg[a] whenever magic[a] is the argument of a function.

Suppose I want arg[a] to be replaced by a, but only when arg[a] is NOT the argument of a function. Ie. I want a rule that applies only on top-level, only in those situations where the first pattern would not apply. Making arg[a_] := a is inadequate. In pseudocode it would be arg[a_] := a /; Not[FunctionArgument[arg[a]]]. Is this possible to achieve in Mathematica?

• What exactly do you mean by "top-level"? Do you actually mean when arg[a] is only at Level 1? In that case, you can use Replace[expr, arg[a] -> a, {1}] to repalce only at level 1. However, I suspect that that's not quite what you mean, because, for instance, Replace[arg[a]^2 + arg[a], arg[a] -> a, {1}] would yield arg[a]^2 + a, and I suspect that you would want the other arg[a] replaced as well. Can you clarify? For instance, what kinds of expressions are you looking to make this replacement in? Commented Sep 3, 2021 at 20:33
• No, actually, Replace[expr, arg[a] -> a, {1}] is correct as you described. I'm not sure if or how it's possible to turn this kind of replace into a rewriting rule. Ie. I want arg[a] to be automatically replaced by a whenever arg[a] is NOT in a pattern of the form f_[x___,arg[a_],y___] . Which is not very often, but it can happen. I suspect the negation goes "against" the usual rules of defining transformation. Commented Sep 3, 2021 at 21:53
• I wonder if this rule would work. We could probably simplify it, but I think this might work. The idea is that ReplaceAll starts from the top level and if it changes a subexpression, it doesn't replace any subexpressions of the subexpression. So we design a rule that matches a general expression at the top level:(f_[x__, arg[a], y___] | f_[x___, arg[a], y__]) :> f[x, a, y] Commented Sep 3, 2021 at 22:06
• Although, that might also do essentially the same thing as magic/:f_[x___,arg[a_],y___]:=f[x,arg[a],y]. Actually, now that I think about it, I'm still a little confused about what you need. Can you give some example inputs and outputs that show the problem cases? Commented Sep 3, 2021 at 22:07

So, one way we could approach this is by inspecting the evaluation stack:

arg[a_] := a /; StackInhibit[Stack[] === {arg, RuleCondition}]


StackInhibit just means we don't have to also worry about matching SameQ, so it's not totally necessary. This could also be subverted by appropriate StackBegins, but that could be viewed as either a bug or a feature: if we want to force arg[a] to "behave like a top level expression", surround it with StackBegin. There might be more robust, general, or elegant ways to get this same behavior.

Let's try it out:

arg[5]

(* Out: 5 *)

x := arg[5]

x

(* Out: 5 *)

f[arg[5]]

(* Out: f[arg[5]] *)


But there are some cases where it doesn't work as you might expect:

Null; arg[5]

(* Out: arg[5] *)

Block[{x}, arg[5]]

(* Out: arg[5] *)


I wonder if there's a way around this.

There is an ad-hoc way to apply a rule to every output expression, by setting $Post: $Post = Replace[arg[a_] :> a];


(Note that Replace by default operates only on the whole expression, unlike ReplaceAll (/.), so we don't need to specify the level.)

While this will fix the examples above, it is local to a given kernel session, has to play nicely with potential other definitions of \$Post, and is not a property of the head arg, so it might not be desirable.