Jay, here's another way of thinking about it. Let's say you have an expression that has some hierarchy to it, like:
A[1, B["a"]]
What Mathematica will try to do is simplify/evaluate this expression according to rewrite rules (UpValues, DownValues, etc). What order should it do this in? Should it apply the rules for A first and then B, or the other way around? It seems intuitive that it should evaluate "inside out", and in fact this is what Mathematica typically tries to do. So, it looks for a rewrite rule for B, applies that, and then moves on to the rewrite rule for A.
Let's define A like this:
A[x_Integer, y_] := {1 + x, y, Head[y]}
Now let's evaluate A[1, B["a"]]
, and we should get {2, B["a"], B}
But if we now define B like this:
B[x_String] := "z"
and re-evaluate A[1, B["a"]]
, we will get {2, "z", String}
. So, B
must have been evaluated first. If you evaluate A[1, B[1]]
, you'll get {2, B[1], B}
, because our rewrite rules for B don't match an integer argument.
Okay, let's try ReplaceAll
:
ReplaceAll[A[1, B[1]], 1 -> 5]
Evaluating that should give {2, B[5], B}
. B[1] was evaluated first, but since there were no matching re-write rules, it just produced B[1], then the re-write rules for A were applied (giving {2, B[1], B}
, and then ReplaceAll was applied, giving the final result.
Let's try
ReplaceAll[A[1, B["a"]], "a" -> 1]
This produces {2, "z", String}
. If ReplaceAll had been applied first, then we would have seen a B[1]
in the result.
Finally, let's try
ReplaceAll[A[1, B[1]], 1 -> "a"]
This produces {2, "z", B}
. Since the ReplaceAll changed the expression, Mathematica does another round of evaluation, and the B["a"]
that would have been there in an intermediate step gets evaluated to just "z" according to B
's rewrite rules.
So, in your original question,
DatePlus[{2021,1,10},x]/.x->-30
everything inside the DatePlus
expression gets looked at, and presumably there were no re-write rules for x, so Mathematica moved on to the full DatePlus
expression. Since it can't evaluate it with a non-numeric value in the second position, it just returns the whole DatePlus
expression unevaluated. However, DatePlus is designed to have the side effect of creating an error message in that case, so you see that error message appear during evaluation. We also must evaluate the expression x->-30
, but this also cannot be simplified, so that bit remains as is. Next, we move on to the containing ReplaceAll
expression. The replacement rule gets applied. Since this changed the expression, the new expression was re-evaluated, and this time DatePlus
could perform the evaluation (without error).
Here's a fun thing to try. Evaluate the following lines:
x = 1;
DatePlus[{2021, 1, 10}, x] /. x -> -30
You should get {2021, -30, 11}
. Walk through the evaluation manually, step by step, and you can see how we got that.