2
$\begingroup$

I noticed the following unexpected result:

In[90]:= "foo" /. x_String -> Head[x]

Out[90]= Symbol

Why is it that Head[x] doesn't return String? I would assume that since the pattern matched on a String head, the right hand side of the rule should see the same thing. Furthermore, the following expression gives the same result (assuming foo is undefined):

In[103]:= foo /. x_Symbol -> Head[x]

Out[103]= Symbol

How does one distinguish strings from symbols in the replacement part of a rule in this kind of situation?

$\endgroup$
  • 5
    $\begingroup$ You should use RuleDelayed: "foo" /. x_String :> Head[x]. Othewise, the r.h.s of your rule is computed before the match, when x is still a symbol, not bound to anything. $\endgroup$ – Leonid Shifrin May 15 '16 at 22:31
  • $\begingroup$ OK, I see how that works. How would you, though, with an eager (->) rule determine the "stringiness" of a particular symbol (i.e. the fact that it's quoted)? BTW, why don't you make this an answer? $\endgroup$ – Adrian May 15 '16 at 23:27
  • $\begingroup$ Ok, I did as you requested. If you read the explanation carefully, it should answer your second question. One thing you should realize is that x has no connection to the string "foo" whatsoever, until the rule application happens. $\endgroup$ – Leonid Shifrin May 16 '16 at 11:19
  • $\begingroup$ I feel that this could be marked as a duplicate of one of the existing Q&A's about Rule vs RuleDelayed, perhaps (22917) or (24860). $\endgroup$ – Mr.Wizard May 16 '16 at 11:26
  • 1
    $\begingroup$ @Mr.Wizard I was trying to find such past discussions, since also suspected there were, but failed. And I agree with you that it would better serve us as a part of the pitfalls discussion, but have exhausted my available time for today - and for the pitfall post, I'd have to generalize a bit, and look also into those other posts, to take good pieces from those. Will do that when I get a moment. $\endgroup$ – Leonid Shifrin May 16 '16 at 11:44
3
$\begingroup$

Short answer

You should use RuleDelayed:

"foo" /. x_String :> Head[x]

Othewise, the r.h.s of your rule is computed before the match, when x is still a symbol, not bound to anything.

Longer answer

Let's enumerate the main steps the main evaluator goes through, when evaluating your original code.

The case of Rule

  • The head ReplaceAll is evaluated (to itself, trivially). Evaluation then goes to the elements (arguments of ReplaceAll), left to right.

  • The first argument, "foo", is trivially evaluated to itself

  • The second argument, Rule[x_String, Head[x]] is evaluated

    • The head Rule is trivially evaluated.

    • Both of its arguments are evaluated, since Rule does not hold arguments

      • x_String is evaluated to itself
      • Head[x] is evaluated to Symbol, since at this time, in your case, x was not assigned any value. If it was, Head[x] would have evaluated to the head of that value.
  • Finally, the internal DownValues for ReplaceAll are allowed to fire, "on the way up" in the recursive evaluation procedure. By this time, the rule you want to apply, is

    x_String -> Symbol
    

    And thus the result.

The case of RuleDelayed

  • The head ReplaceAll is evaluated (to itself, trivially). Evaluation then goes to the elements (arguments of ReplaceAll), left to right.

  • The first argument, "foo", is trivially evaluated to itself

  • The second argument, RuleDelayed[x_String, Head[x]] is evaluated

    • The head RuleDelayed is trivially evaluated.

    • Arguments of RuleDelayed start to evaluate from left to right. However, RuleDelayed is HoldRest, and that is important here.

      • x_String is evaluated to itself
      • Head[x] is not evaluated at this time, due to the HoldRest attribute of enclosing RuleDelayed.
  • The internal DownValues for ReplaceAll are allowed to fire, "on the way up" in the recursive evaluation procedure. By this time, the rule you want to apply, is

    x_String :> Head[x]
    

    Now, ReplaceAll is in the position to bind the value of x on the r.h.s. with the result found during the pattern-matching of the pattern on the l.h.s. against your expression ("foo" in this case). This results in a new expression Head["foo"]

  • Finally, Head["foo"] evaluates to String.

Can we get the right result with Rule?

To answer your specific question in comments, it also should be clear from the above discussion, that generally one should use RuleDelayed to get the right result, instead of Rule.

One thing you can do, if you insist on using Rule, is the following:

"foo" /. Unevaluated[x_String -> Head[x]]

(* String *)

This works in this case, but note that in this approach, x_String does not evaluate either. This is Ok here, but it is important to note the difference.

Summary

There are just a few things one needs to remember about Rule vs RuleDelayed:

  • Use Rule, if you either

    • Do want the r.h.s. to evaluate before the pattern matching / replacement happens
    • Don't care, because for example the r.h.s of the rule is a constant expression, not containing any variables
  • Use RuleDelayed in the majority of cases, when you have pattern variables on both sides of the rule.

So in general, when your patterns contain variables, it is RuleDelayed you typically want to use, not Rule.

$\endgroup$
  • 1
    $\begingroup$ You have my vote, as usual, but I think this might serve the community better filed under the Pitfalls post. We certainly have multiple questions originating from the same problem but the nature of these makes them hard to search for. (I only found two to link above though I know there are more.) $\endgroup$ – Mr.Wizard May 16 '16 at 11:30
  • $\begingroup$ @Mr. Wizard, I believe this is the second or third question of this sort I've seen in the past two or so weeks. $\endgroup$ – J. M. will be back soon May 16 '16 at 11:39

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.