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
:
So in general, when your patterns contain variables, it is RuleDelayed
you typically want to use, not Rule
.
RuleDelayed
:"foo" /. x_String :> Head[x]
. Othewise, the r.h.s of your rule is computed before the match, whenx
is still a symbol, not bound to anything. $\endgroup$ – Leonid Shifrin May 15 '16 at 22:31->
) 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:27x
has no connection to the string"foo"
whatsoever, until the rule application happens. $\endgroup$ – Leonid Shifrin May 16 '16 at 11:19Rule
vsRuleDelayed
, perhaps (22917) or (24860). $\endgroup$ – Mr.Wizard May 16 '16 at 11:26