12
$\begingroup$

I'm interested in creating a function or rule that generates its own set of rules. For instance, it would take as an input the expression h[m, n] and the indices m and n, and output the rule:

h[m_, n_] :> h[m, n]

What I have is:

makeLHSReplacementRule[expr_, {m_,n_}] := 
  ((expr /. m -> head[m] /. n -> head[n]) /. head[a_] :> head[a_]) /. head[A_] :> A

replacementRule[expr_, {m_, n_}] := makeLHSReplacementRule[expr, {m, n}] :> expr  

replacementRule[h[m, n], {m, n}]

The output is actually exactly what I want, which is:

h[m_, n_] :> h[m, n]

But I get an error message complaining about my having the pattern a_ appearing on the right hand side of a rule. This is making me think it might not be safe to do this (it is also very annoying, as I need to run this function many times). Does anyone know a way around this?

$\endgroup$
  • 1
    $\begingroup$ Replace head[a_] :> head[a_] with head[a_] :> With[{b = a}, head[b_]]? $\endgroup$ – kglr Jul 30 '16 at 23:18
11
$\begingroup$

RuleDelayed::rhs is not an error message but rather a warning message. This is an important distinction. If you know what you are doing the message can be safely ignored. This is also the case with Pattern::patv.(1)(2)

I would say that your method as written is not safe however for a different reason: it evaluates the Symbols m and n which means your code breaks if these are defined. Please consider this as an alternative:

SetAttributes[replacementRule, HoldAll]

replacementRule[expr_, {var__Symbol}] :=
 (Unevaluated[expr] /.
    Thread[Thread @ HoldPattern @ {var} -> Thread @ Pattern[{var}, _]]) :> expr

Test:

m := Print["fail!"];

replacementRule[h[m, n], {m, n}]
h[m_, n_] :> h[m, n]

Or instead using Block to guard against evaluation:

ClearAll[replacementRule]

SetAttributes[replacementRule, HoldAll]

replacementRule[expr_, var : {__Symbol}] :=
  Block[var,
    Unevaluated[expr] /. Thread[var -> (Pattern[#, _] & /@ var)]
  ] :> expr

Further thoughts

Both examples above manage to avoid RuleDelayed::rhs by not having an explicit var_ in the body of the definition, but that should not be taken to mean that this is actually superior to methods that do trigger the warning.

Here is a third approach that is more similar to your original method:

ClearAll[replacementRule]
Attributes[replacementRule] = {HoldAll};

replacementRule[expr_, {var__Symbol}] :=
  (Unevaluated[expr] /. x : HoldPattern[Alternatives[var]] :> x_) :> expr

This is somewhat more streamlined than my first method with repeated use of Thread but it throws a warning message when used:

replacementRule[h[m, n], {m, n}]

RuleDelayed::rhs: Pattern x\$_ appears on the right-hand side of rule x\$:HoldPattern[m|n]:>x\$_. >>

h[m_, n_] :> h[m, n]

(The appearance of x$ comes from the automatic renaming that takes place in nested scoping constructs of which RuleDelayed is one, despite its not being specifically advertised as such. I started an answer which Leonid Shifrin greatly expanded regarding this that you should eventually read.)

It would in my opinion be entirely acceptable to suppress that warning with Quiet:

ClearAll[replacementRule]
Attributes[replacementRule] = {HoldAll};

replacementRule[expr_, {var__Symbol}] := 
  Quiet[
    Unevaluated[expr] /. x : HoldPattern[Alternatives[var]] :> x_, 
    RuleDelayed::rhs
  ] :> expr

In retrospect I think I prefer this to my earlier recommendation.

$\endgroup$
  • $\begingroup$ Thanks very much for the advice! $\endgroup$ – pploops Jul 30 '16 at 23:54
  • 1
    $\begingroup$ @pploops You're welcome. Let me know if you have any questions about my sometimes opaque code. $\endgroup$ – Mr.Wizard Jul 30 '16 at 23:55
  • $\begingroup$ Regarding your final method, could you explain what is the purpose of HoldPattern - what does it safeguard again precisely? Also is the use of Symbol in var__Symbol just good practice? Thanks so much for answering these questions! $\endgroup$ – pploops Jul 31 '16 at 10:09
  • 1
    $\begingroup$ @pploops HoldPattern, when combined with HoldAll for replacementRule, prevents the Symbols like m and n from evaluating. I used m := Print["fail!"]; earlier in the post to illustrate this. In your application this may not matter but it is better to think about such things if you want your code to be reusable. This is not without consequence however as due to HoldAll one cannot do e.g. foo = h[m, n] and then replacementRule[foo, {m, n}]. Possibly a better choice would be HoldRest assuming that head h has a Hold attribute itself in which case this example would work. $\endgroup$ – Mr.Wizard Jul 31 '16 at 10:24
  • 1
    $\begingroup$ @pploops I did not want to complicate my answer further with this but am happy to describe it further if requested. My use of __Symbol is what I would consider good practice. While it is not strictly necessary it will catch problems that might otherwise be confusing. $\endgroup$ – Mr.Wizard Jul 31 '16 at 10:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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