While writing this question, I've come up with the following workaround:
r4 = x_ /. p : Verbatim[Pattern][a_, _] :>
((p :> Evaluate@HoldComplete[1 -> a]) /. HoldComplete -> Association)
(* x_ :> Association[1 -> x] *)
f /. r4
(* <|1 -> f|> *)
So as long as RuleDelayed
does not see Association
, everything is fine.
Update
@WReach pointed me to a clearer and more in-depth explanation of what's happening here. I think that answer covers everything from my questions.
My attempt at an explanation
It seems like as soon as the Association[...]
expression is evaluated, it is replaced by an "atomic" object, that no longer allows RuleDelayed
to "see" the values. (From here, we know that Assocation
s have the attribute HoldAllComplete
, but it seems it's more severe than that)
The confusion now arises since this "atomic object" is always displayed in it's InputForm
(well, short of the Association[...]
vs. <|...|>
thing), and reentering this form consequently fixes the issue, as the HoldRest
attribute of RuleDelayed
prevents a new object from being created at this point.
The only point I can't answer at all is "how to tell the difference between the two outputs", and I'm hoping someone else can provide a solution to this.