# Replace vs. ReplaceAll involving Flat

I'm aware that Replace and ReplaceAll behave differently as discussed in this question, but still get confused with the following example when Flat is involved:

f//Attributes = {Flat, OneIdentity};

Replace[f[x, 2 y, z], f[x_, 2 y_] :> f[x, y], {0, Infinity}]
(* f[x, 2 y, z] *)

ReplaceAll[f[x, 2 y, z], f[x_, 2 y_] :> f[x, y]]
(* f[x, y, z] *)

The result of ReplaceAll is as expected, but why does Replace not try the pattern f[f[x, 2y], z]? Is there a simple workaround for Replace without modifying the rule?

Update 1

A simpler example by @user293787:

SetAttributes[f, Flat];
ReplaceAll[f[a,b],f[a]->0]
(*f[0,b]*)
Replace[f[a,b],f[a]->0,All]
(*f[a,b]*)

Update 2

It seems that in the sequence inside f, a non-Pattern head will stop the matching from left to right, e.g.

Replace[f[a,b,c],f[a_,b]->g[a,b],All]
(*f[a, b, c]*)
Replace[f[a,b,c],f[a_,c]:>g[a,c],All]
(*g[f[a, b], c]*)

(the update 2 is from part of my answer, in which the solution is totally wrong. I delete it to avoid confusions.)

• That is an interesting observation. Related: f[a,b]/.f[a]->0 gives f[0,b] but Replace[f[a,b],f[a]->0,{0,Infinity}] gives f[a,b]. Commented Sep 16, 2022 at 17:29
• Tangentially related: In this post from 9 years ago they say that SetAttributes[M,{Flat}];Replace[M[F1[a]],M[x_F1]|M[x_F2]->x] returns M[F1[a]], but if I run it now I get F1[a]. Apparently this was "fixed". Perhaps you found a similar problem. Commented Sep 16, 2022 at 17:40
• @user293787, thx! I'll add your simpler example to the question. Commented Sep 16, 2022 at 17:51
• @user293787 Yes, that was fixed. Or possibly "fixed", as you put it; I'm never certain about these changes when it comes to the pattern matcher. Commented Sep 18, 2022 at 16:11

For a way to explain the behavior, see @MichalE2's answer

The following might be a workaround, at least for the examples in the question. It is based on the observation that a catch-all rule like x_:>x stops ReplaceAll from matching any parts not on the top level, but doesn't stop "Flat-induced" matches:

f // Attributes = {Flat, OneIdentity};

ReplaceAll[f[x, 2 y, z], {f[x_, 2 y_] :> f[x, y], x_ :> x}]
(* f[x, y, z] *)

ReplaceAll[{f[x, 2 y, z]}, {f[x_, 2 y_] :> f[x, y], x_ :> x}]
(* {f[x, 2 y, z]} *)

Thus:

myReplace[expr_, rules_, level_ : {0}] :=
Map[ReplaceAll[Flatten@{rules, x_ :> x}], expr, level]

myReplace[f[x, 2 y, z], f[x_, 2 y_] :> f[x, y]]
(* f[x, y, z] *)

myReplace[{f[x, 2 y, z]}, f[x_, 2 y_] :> f[x, y]]
(* {f[x, 2 y, z]} *)

myReplace[{f[x, 2 y, z]}, f[x_, 2 y_] :> f[x, y], 1]
(* {f[x, y, z]} *)
• Nice. (+1) I've used this trick in other situations. Somehow if replacement is on my mind, I usually do myReplace like this: myReplace[expr_, rules_, level_ : {0}] := Replace[expr, e_ :> (e /. Flatten@{rules, x_ :> x}), level]. I can't think of any advantage of one over the other, though. Commented Sep 18, 2022 at 14:53
• @MichaelE2 I guess a minor advantage is the fact that it is more explicit that the level specification is applied correctly. On the other hand, I do find it slightly harder to mentally parse. Other than that, they should indeed behave equivalently as far as I can tell Commented Sep 18, 2022 at 15:55
• I didn't realize there was any difference in how they handled the level spec. (Or I've forgotten the difference.) Commented Sep 18, 2022 at 16:25
• @MichaelE2 Sorry for the confusion - I meant to say that while the levelspec behavior is the same for Map and Replace (as far as I know), it's more obvious that you get the correct behavior if you do actually use Replace Commented Sep 18, 2022 at 17:11

why does Replace not try the pattern f[f[x, 2y], z]?

First, I don't think ReplaceAll does this either. It matches the argument sequence x, 2y by virtue of the Flat attribute and replaces it by f[x, y] according to the OP's rule. Alternatively, we could look at it as matching Part[f[x, 2y, z], ;; 2]. Either, I don't think pattern matcher unflattens a flat function. (This is undocumented AFAIK, so I could be mistaken.)

It can do something like the operation in the quote above when carrying out the replacement. Using TracePrint on f[w, x, 2 y, z] and f[v, w, x, 2 y, z], we see that x_ in f[x_,2 y_]:>f[x,y] matches w, x and replaces them with Part[f[w, x, 2y, z][[;; 2] or f[w, x], and similarly for v, w, xandf[v, w, x]`.

f[w,x,2 y,z] /. f[x_,2 y_]:>f[x,y]
f[f[f[w,x],y],z]

f[v,w,x,2 y,z] /. f[x_,2 y_]:>f[x,y]
f[f[f[v,w,x],y],z]

Next to the docs for Replace:

Replace by default applies rules only to complete expressions

Of course x, 2y is not a complete expression, and arguably Part[f[x, 2 y, z], ;; 2] is part of an expression, not a complete expression at any level of f[x, 2y, z].

Unfortunately, the site users who seemed to have authoritative knowledge of the pattern matcher also seem to have left the site. :(

Workaround?

I'd use this, but maybe I'm missing something about what the OP wants:

Replace[f[x, 2 y, z], f[x_, 2 y_, foo___] :> f[x, y, foo]]
• +1. While I think your explanation makes sense, I don't like what it is implying. In my mind, Replace is a more controlled version of ReplaceAll. However, this behavior means that it is impossible to faithfully apply DownValues of a symbol at the top-level only (besides trying to check the expression for its head beforehand or similar). Also, the documentation is not really clear on this (as you point out): For instance, from the documentation of Flat: "Flat is an attribute... This property is accounted for in pattern matching." - the latter doesn't really seem to be true here Commented Sep 17, 2022 at 23:56
• @LukasLang Yes, that seems to be the case. Commented Sep 18, 2022 at 1:33
• Thx! In my actual usage the rule is a long list. Commented Sep 18, 2022 at 9:46
• I was unaware that anyone had authoritative knowledge of the pattern matcher. I'm not sure such a background can exist, though to be fair I'm not aware of any non-existence proof either. (Me, I just fix the bugs, I don't pretend to understand much of it. In particular I don't understand the inner doings of Flat which, as best I can tell, is the pattern matcher's equivalent to the human limbic system.) Commented Sep 18, 2022 at 16:15
• @DanielLichtblau They seemed to know. And when they corrected me, often rather quickly, they seemed to be right. How they came by that knowledge, I don't know. Whether one wants to call it "authoritative," or "divine," or whatever, their knowledge exceeded mine and would still, I believe. (It was a wistful remark recalling good times, which also was meant to invite anyone with a different understanding to disagree and educate me.) Commented Sep 18, 2022 at 16:40