2
$\begingroup$

I'm trying to create a function that builds a replacement rule from a given pattern and I have trouble understanding what's going wrong.

After having written most of the question, I see that it got quite long... For those who want to read the questions first, they're at the bottom. I have also come up with a workaround and possible explanation (I'll post a partial answer to this shortly), but I would still like to understand the issue at hand better.

The goal

For this example, the goal is as follows: Given a patten x_, output x_ :> <|1 -> x|> (should work also for y_, etc.)

My approach

This here does the job just fine:

r1 = x_ /. p : Verbatim[Pattern][a_, _] :> p :> <|1 -> a|>
(* x_ :> Association[1 -> x] *)

Since I need it for later, let's Evaluate the r.h.s of the pattern:

r2 = x_ /. p : Verbatim[Pattern][a_, _] :> p :> Evaluate@<|1 -> a|>
(* x_ :> <|1 -> x|> *)

Notice the change from Association[...] to <|...|>.

The issue

Let's try out those rules on a simple example:

y /. r1
(* <|1 -> y|> *)

y /. r2
(* <|1 -> x|> *)

My attempts at an explanation

It seems something went wrong in the second case. Let's try to see how the two outputs differ:

e1 = Block[
    {$ContextPath = {"test`"}, $Context = "test`"},
  ToString@FullForm@(x_ /. p : Verbatim[Pattern][a_, _] :> p :> <|1 -> a|>)
  ]
(* "System`RuleDelayed[System`Pattern[Global`x, System`Blank[]], System`Association[System`Rule[1, Global`x]]]" *)

e2 = Block[
    {$ContextPath = {"test`"}, $Context = "test`"},
  ToString@FullForm@(x_ /. p : Verbatim[Pattern][a_, _] :> p :> Evaluate@<|1 -> a|>)
  ]
(* "System`RuleDelayed[System`Pattern[Global`x, System`Blank[]], System`Association[System`Rule[1, Global`x]]]" *)

e1 == e2   
(* True *)

r1 === r2
(* False *)

So, apparently, all symbols belong to the same contexts in both rules, but they're still somehow different. Another strange thing:

r2
r3 = x_ :> <|1 -> x|> (* using the output from the line above and prepending 'r3 = ' *)
(* x_ :> Association[1 -> x] *)

f /. r3
(* <|1 -> f|> *)

So simply reentering the output is enough to get it working.

The questions

  • What about Association is so special that the second approach fails? (As far as I can tell, it's at least not a scoping construct)
  • How do I prevent this issue?
  • For the future, how can I debug an issue like this? (i.e. how do I find out what's different between r1 and r2)
$\endgroup$
4
  • $\begingroup$ similar: (148074) $\endgroup$
    – WReach
    Commented Mar 17, 2018 at 15:19
  • $\begingroup$ @WReach Thanks for the link - your answer there clears up the rest of my questions $\endgroup$
    – Lukas Lang
    Commented Mar 18, 2018 at 18:23
  • $\begingroup$ @WReach Since your answer in the linked question also answers this question, should I close this as duplicate (since the answer is already given) or leave it and just link to that answer (since the question is arguably a different one)? $\endgroup$
    – Lukas Lang
    Commented Mar 18, 2018 at 18:28
  • $\begingroup$ There does seem to be a lot of overlap. I have registered a close vote and we shall see what the community thinks. $\endgroup$
    – WReach
    Commented Mar 18, 2018 at 18:48

2 Answers 2

1
$\begingroup$

It seems that you are complicating things by attempting to over specify the behavior. Unless I have misunderstood your intent I believe the following will do.

ClearAll[patternReplaceRule]
patternReplaceRule[p : Verbatim[Pattern][s_, _]] := p :> <|1 -> s|>

Creating the rules

r1 = patternReplaceRule[x_]
x_ :> Association[1 -> x]
r2 = patternReplaceRule[y_]
y_ :> Association[1 -> y]

Applying the rules produce the expected result.

x /. # & /@ {r1, r2}
{<|1 -> x|>, <|1 -> x|>}
y /. # & /@ {r1, r2}
{<|1 -> y|>, <|1 -> y|>}

Also

r3 = patternReplaceRule[x_];
r1 == r3
True

Hope this helps.

$\endgroup$
3
  • $\begingroup$ I like the idea of using a SetDelayed rule to insert the pattern name into a held association - unfortunately I don't see how this can scale to my real use-case, which requires me to build an association out multiple individual matches. (i.e. find all Pattern[...] expressions in the input and build one association) $\endgroup$
    – Lukas Lang
    Commented Mar 18, 2018 at 18:21
  • $\begingroup$ @Mathe172 You don't mention any of that in your question so how can you expect a solution that considers it? $\endgroup$
    – Edmund
    Commented Mar 18, 2018 at 23:41
  • $\begingroup$ Good point - I seem to have over simplified the problem when making the MWE, sorry for that. I hope my last comment didn't appear unthankful - even if I can't immediately apply it, your answer still helps to gain a deeper understanding of the underlying problem - so thanks again! $\endgroup$
    – Lukas Lang
    Commented Mar 19, 2018 at 12:45
1
$\begingroup$

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 Assocations 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.

$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.