I think this is a product of Mathematica's lexical-scoping algorithm being efficient and just scoping anything that comes into comes into contact with a scoped variable (i.e. t
in this case).
Here's some data that may or may not back that up.
First, the case at hand
Function[{t},
{
q_ -> {q, t},
z_ -> x,
z_ :> x,
t_ :> t
}
][1]
{q$_ -> {q$, 1}, z_ -> x, z_ :> x, t_ :> t}
We see that for the other scoped symbols, nothing happens. Interestingly, Function
is not doing value injection, since the second t
isn't affected.
Compare that to a regular function definition
meh[t_] :=
{
q_ -> {q, t},
z_ -> x,
z_ :> x,
t_ :> t
};
meh[1]
(* RuleDelayed::rhs: Pattern t_ appears on the right-hand side of rule meh[t_]:>{q_->{q,t},z_->x,z_:>x,t_:>t}. *)
{q$_ -> {q$, 1}, z_ -> x, z_ :> x, Pattern[1, _] :> 1}
We see clearly that the same lexical scoping happens, but in this case the t
value is injected everywhere.
Now trying this with With
, which should do pure injection
With[{t = 1},
{
q_ -> {q, t},
z_ -> x,
z_ :> x,
t_ :> t
}
]
{q$_ -> {q$, 1}, z_ -> x, z_ :> x, t_ :> t}
we see that the t
is actually untouched. My hypothesis is that the lexical scoping algorithm has a set of heads that it doesn't touch, and the LHS of Rule
is one of them. I know Rule
and RuleDelayed
have a lot of special properties like SequenceHold
. This might just be another one of those.
For the purest injection possible, we can use ReplaceAll
ReplaceAll[
{
q_ -> {q, t},
z_ -> x,
z_ :> x,
t_ :> t
},
t :> 1
]
{q_ -> {q, 1}, z_ -> x, z_ :> x, Pattern[1, _] :> 1}
this does no scoping and just replaces t
wherever it sees it.
Just to round things out, Block
/Module
behave exactly like With
, which is unsurprising given that Block
respects Hold
and the scoping algorithm used in With
is the same one as in Module
(I think) but with different scoped names.
Block[{t = 1},
{
q_ -> {q, t},
z_ -> x,
z_ :> x,
t_ :> t
}
]
{q_ -> {q, 1}, z_ -> x, z_ :> x, t_ :> t}
Module[{t = 1},
{
q_ -> {q, t},
z_ -> x,
z_ :> x,
t_ :> t,
t_ -> t
}
]
{q$_ -> {q$, 1}, z_ -> x, z_ :> x, t_ :> t, t_ -> t}
This is not a definitive answer by any means, but I think it provides some data in favor of it just being the way Mathematica does lexical scoping.
I'm guessing that this is just an efficient way to do this, given that the internal MExpr
object that represents Mathematica expressions (totally conjecture that there is one, but conjecture based on lots of hints and tips from WRI employees) can be treated as a tree and therefore can probably efficiently tell you what symbols are in and expression, but can't exactly tell you where. Easier to scope all of the symbols than to walk the tree and replace only the ones touched by the scoping construct explicitly.
One final bit of input, I think Function
is smart when it's given t
as an input and simply returns the function body without scoping. This is just an efficient short-cut for a common case. Here's my data for that statement
Function[
{t},
{
q_ -> {q, t},
z_ -> x,
z_ :> x,
t_ :> t
},
HoldAll
][t]
{q_ -> {q, t}, z_ -> x, z_ :> x, t_ :> t}
t = 5;
Function[
{t},
{
q_ -> {q, t},
z_ -> x,
z_ :> x,
t_ :> t
},
HoldAll
][t]
{q_ -> {q, 5}, z_ -> x, z_ :> x, t_ :> t}
as long as it sees t
explicitly as input, it doesn't scope q
.
With[{t = 2}, t_ -> t]
also fails to make the replacement. $\endgroup$