In many tensorial calculations in , we see formal symbols like z. or extended formal symbols like λ.123. Formal symbols, extended or not, are like ordinary symbols, except they’re protected: you can’t assign values to them without purposefully unprotecting them. That feature saves accidental assignment to indices on tensors, which must remain fully symbolic for to do its work.
Mathematica directly supports “Pure System” formal symbols like α., consisting of exactly one formal character. All such formal characters are listed below. needs extended formal symbols, which have some non-formal characters after an initial formal character, for Unique protected symbols, guaranteed not to collide with other symbols of any kind.
Wolfram does not offer FormalSymbolQ, so we implement it here. Extended formal symbols are a invention: Wolfram knows nothing about them, so we implement a constructor, CreateExtendedFormal, and a predicate, FormalSymbolExtendedQ. A formal symbol is reckoned “Extended” if it has correct syntax—a formal symbol followed by non-formals—and has the Protected attribute. It’s possible to create syntactically correct but unprotected extended look-alikes, here’s one: a.42. It’s not protected, so won’t use it, and you shouldn’t either.
A symbol can be a pure formal symbol, an extended formal symbol, or neither, but never both pure and extended.
We further implement a SymbolInspector for human monitoring and debugging of formal symbols. The creation of SymbolInspector ballooned into a full-blown, self-taught master class in evaluation control. The last section of this notebook presents our learning in detail for anyone who wants to wade in those waters.
The functions and tests in this notebook have been added to the Relativity Toolkit, and the rest of the toolkit has been retrofitted to use such disciplined formals.
Initialize the Relativity Toolkit
Initialize the Relativity Toolkit
Optional: Quit for a Fresh Kernel
Optional: Quit for a Fresh Kernel
◼
Note, if you call Quit in a notebook, evaluation may hang. If you want to “Evaluate Notebook,” mark the following two cells “Evaluatable” in the CellProperty menu, call Quit and FrontEndTokenExecute once each, then comment out the cells, or mark them unevaluatable again. Alternatively, you can quit the kernel via the Evaluation menu (last item). At that point, you can “Evaluate Notebook” in a clean environment, or evaluate cells individually.
->
Quit[];
In[]:=
FrontEndTokenExecute["DeleteGeneratedCells"]
Download the Code
Download the Code
In[]:=
RTbranch="v1.13.1";RTbase="https://raw.githubusercontent.com/rebcabin/RelativityToolkit/"<>RTbranch<>"/";RTbust="?t="<>ToString[Round[AbsoluteTime[]]];(*Forcefreshdownload*)RTrules=RTbase<>"RelativityToolkit.wl"<>RTbust;RTtests=RTbase<>"RelativityToolkit_RegressionTests.wl"<>RTbust;RTviz=RTbase<>"RelativityToolkit_VisualShowcase.wl"<>RTbust;Get[RTrules];
»
Relativity Toolkit version :1.13.1
»
Relativity Connection Symbol :Γ
We must evaluate the regression test suite to load the test harness “Assert...” functions, which are exercised below in the body of this notebook.
In[]:=
Get[RTtests];
================================================================
RUNNING REGRESSION SUITE v1.13.1
================================================================
--- SECTION 1: TYPE CHECKING (VALENCE) ---
[PASS] 1: Vector () ⟶ {{μ},{}}
μ
x
[PASS] 2: Covector () ⟶ {{},{ν}}
p
ν
[PASS] 3: Mixed Tensor () ⟶ {{μ},{ν}}
μ
T
ν
[PASS] 4: Mixed Tensor () ⟶ {{},{μ,ν}}
T
μν
[PASS] 5: Mixed Tensor () ⟶ {{μ,ν},{}}
μν
T
[PASS] 6: Mixed Tensor ⟶ {{ν},{μ}}
ν
T
μ
[PASS] 7: Bare Symbol T ⟶ {{},{}}
[PASS] 8: Bare Call T[] ⟶ {{},{}}
[PASS] 9: Trap (Null) ⟶ {{},{}}
[PASS] 10: Scalar Contraction ⟶ {{},{}}
μ
x
p
μ
[PASS] 11: Scalar Contraction ⟶ {{},{}}
μ
x
p
μ
[PASS] 12: Tensor-Vector ( ) ⟶ {{μ},{}}
ν
x
μ
T
ν
μ
x
[PASS] 13: Tensor-Vector ( ) ⟶ {{},{ν}}
μ
x
μ
T
ν
x
ν
[PASS] 14: Power of Scalar ⟶ {{},{}}
[PASS] 15: Scalar Multiplication ⟶ {{μ},{}}
[PASS] 16: Gradient d(f)/ -> Down[α] ⟶ {{},{α}}
α
dx
Valence Mismatch
»
expect error message above{{},{}}
[PASS] 17: Mismatch Handled Gracefully ⟶ {{},{}}
---SECTION2:ALGEBRAICCANONICALIZATION---
[PASS] 18: Dummy Renaming ⟶ p(x)
μ
A
B
μ
ν
A
B
ν
i.1
i.1
[PASS] 19: Commutativity ⟶ p(x)
μ
A
B
μ
B
ν
ν
A
i.1
i.1
[PASS] 20: Index Coalescing + 2 ⟶ 2p(x)
μ
A
B
μ
ν
A
B
ν
ρ
A
B
ρ
i.1
i.1
[PASS] 21: Double Dummy Pairs ⟶ (P)Q(S)T
μ
P
ν
S
Q
μ
T
ν
i.1
i.1
i.2
i.2
[PASS] 22: Free Indices Preserved ⟶ (A)(B),(A)(B)
α
β
i.1
i.1
--- SECTION 3: PHYSICS, METRIC & TRANSFORMATIONS ---
[PASS] 23: Lowering Index ⟶ A
ν
A
g
μν
A
μ
μ
[PASS] 24: Raising Index ( ) ⟶ p
μν
g
p
ν
μ
p
μ
[PASS] 25: Inverse Metric ( ) ⟶
μα
g
g
αν
μ
δ
ν
μ
δ
ν
[PASS] 26: Alpha-Conversion: distinct indices) ⟶ μ.11,μ.12
[PASS] 27: Triple Metric Sandwich (g.g.g -> g) ⟶ g
μ
ν
[PASS] 28: Delta-Gamma Contraction ⟶
s
Γ
ab
=== Metric Differentiation Rules ===
[PASS] 29: Permissive Contraction ⟶
nu
δ
lam
[PASS] 30: Metric Symmetry in Derivatives ⟶ 0
[PASS] 31: Delta Chain Rule ⟶
f[x]
,μ
[PASS] 32: Metric Compatibility ⟶ 0
[PASS] 33: Levi-Civita Index Safety (Unique indices generated) ⟶ σ.17,σ.18
--- SECTION 4: METRIC EQUIVALENCE ---
μ
A
B
μ
ν
B
A
ν
[PASS] 34: Structural Distinction ≠ initially ⟶ (A)B,A(B)
μ
A
B
μ
ν
B
A
ν
i.1
i.1
i.1
i.1
[PASS] 35: Metric Equivalence reduces to ⟶ (A)B
A
ν
ν
B
μ
A
B
μ
i.1
i.1
--- SECTION 5: SECOND DERIVATIVE BOX STRUCTURE ---
[PASS] 36: Found FractionBox + ^2 ⟶ True
--- SECTION 6: CALCULUS ENGINE ---
[PASS] 37: Leibniz Product Rule ⟶ g+f
f
,μ
g
,μ
[PASS] 38: Covariant Derivative Expansion ⟶ True
[PASS] 39: Power Rule ⟶ 2a$8002
i.1
a$8002
i.1
,ν
[PASS] 40: Differentiation Linearity ⟶ True
[PASS] 41: CD of Scalar reduces to Partial ⟶
phi$8014
,μ
[PASS] 42: Scalar CD contains no Connection Coefficients ⟶ True
[PASS] 43: Nested CD Alpha-Conversion (Unique Indices Generated) ⟶ True
--- SECTION 7: QUOTIENT THEOREM (Extraction) ---
[PASS] 44: Quotient Extraction (Simple) ⟶ T
s.
[PASS] 45: Quotient Extraction (Distributed Dummies) ⟶ R+S
s.
s.
[PASS] 46: Quotient Extraction (Zero Case) ⟶ 0
--- SECTION 8: TENSOR FORM ROBUSTNESS ---
[PASS] 47: TensorForm maps arbitrary formals (Q, Z, I99) to Greek ⟶ True
--- SECTION 9: COMMA NOTATION DISPLAY LOGIC ---
[PASS] 48: Atomic Tensor ⟶
A
μ
,ν
[PASS] 49: Sealed Product (Gamma Glue) ⟶
(A)
μ
a
Γ
bc
,ν
[PASS] 50: Sealed Sum ⟶
(A+B)
,ν
[PASS] 51: Sealed Power ⟶
()
2
A
,ν
[PASS] 52: Arbitrary Function (Sin) ⟶
Sin[theta]
,ν
[PASS] 53: Nested Math (A*(B+C)) ⟶
(A(B+C))
,ν
--- SECTION 10: EINSTEIN SUMMATION (Contract & ContractAll) ---
[PASS] 54: Single Contraction (A^u B_u) ⟶ 2(A)B
i.1
i.1
[PASS] 55: Contract (One-Shot) expanded exactly one pair ⟶ {False,True}
[PASS] 56: ContractAll expanded all pairs recursively ⟶ True
[PASS] 57: Variance Handling (Divergence: d(A^u)/dx^u) ⟶ 2
A
i.1
,i.1
[PASS] 58: Distribution over Sum (A^u B_u + C^v D_v) ⟶ True
[PASS] 59: Free Indices Protected (No Contraction) ⟶ (A)(B)
μ
ν
[PASS] 60: Coordinates Excluded from Scanner ⟶ A
1
--- SECTION 11: ELECTRODYNAMICS & GAUGE INVARIANCE ---
[PASS] 61: U(1) Gauge Covariance ψ ψ ⟶ -e$8113ψ$8113(A$8113)+
D
μ
′
)
eΛ
D
μ
e$8113Λ$8113
μ
e$8113Λ$8113
ψ$8113
,μ
[PASS] 62: Field Strength Invariance (F' == F) ⟶ -()+
A$8113
m
,n
A$8113
n
,m
[PASS] 63: Weinberg-Salam Mixed Covariance (Geometric + Gauge) ⟶ -gW$8128(W$8128)(Z$8128)+W$8128
gW$8128Λ$8128
W$8128
α
,μ
gW$8128Λ$8128
α
μ
gW$8128Λ$8128
i.1
α
Γ
μi.1
--- SECTION 12: RIEMANN CURVATURE DERIVATION ---
[PASS] 64: Riemann Derivation matches Textbook ⟶ --+
μ
Γ
s.ν
,ρ
μ
Γ
s.ρ
,ν
i.1
Γ
s.ρ
μ
Γ
i.1ν
i.1
Γ
s.ν
μ
Γ
i.1ρ
Formal Symbol Character Ranges
Formal Symbol Character Ranges
There are 168 of them:
They live in a few ranges of character codes high in the Unicode sphere. showCodes, below, generates a visual map of the character codes used by Wolfram for formal symbols.
◼
Red Tiles: Unassigned or invalid slots (gaps in the sequence).
The visualization iterates through the integer ranges and queries formalCharCodeQ for every number to decide whether it should be colored Green (Valid) or Red (Invalid).
Check Formal Identity
Check Formal Identity
checkFormalIdentity is a low-level, polymorphic interrogator of the structure of an input Symbol or String. It does not evaluate its input. It returns one of “Pure”, “Extended”, or “Neither”. It fails loudly (with $Failed) on any other type of input.
checkFormalIdentity has a two-layer defense against evaluation, critical when inspecting symbols that might have values assigned.
1
.SetAttributes[..., HoldFirst]: stops the argument s from evaluating before it enters the function. Without this, calling the function on a variable x would pass the value of x instead of x.
2
.Unevaluated[...]: Even inside a HoldFirst function, passing s to certain other functions can trigger evaluation. Wrapping occurrences of s in Unevaluated, e.g., SymbolName[Unevaluated[s]], guarantees that the called function operates on a symbol itself, not its value. Unevaluated has no effect on a String.
Unit tests appear below.
Formal Symbol Predicate
Formal Symbol Predicate
Except for the HoldAll attribute, this implementation is obvious, given checkFormalIdentity.
Extended Formal Symbol Predicate
Extended Formal Symbol Predicate
One must write FormalSymbolExtendedQ[Evaluate[x]] to check the value of an expression x, even if that expression is just a symbol whose value is a constructed extended formal symbol. Many examples and tests below should make this clear.
Constructor for Extended Formal Symbols
Constructor for Extended Formal Symbols
CreateExtendedFormal creates a formal symbol, extended if there are non-formals after a leading formal, pure if there is only a single formal. I.e., it has no effect if given a pure formal.
Test Suite
Test Suite
This has been added to the regression suite of the Relativity Toolkit.
Symbol Inspector
Symbol Inspector
Design & internal Logic
Design & internal Logic
Purpose
Purpose
For displaying and debugging formal symbols, including our extended formal symbols, SymbolInspector exposes the internal properties of any symbol, indirecting when necessary when its input evaluates to the formal symbol we want, metaphorically treating the input as a pointer.
Usage
Usage
◼
On a Literal: SymbolInspector[x] inspects x.
◼
On an Expression: e.g., SymbolInspector[var], evaluates the expression var first, then inspects the resulting value, which may be the value we want.
Examples
Examples
Inspect a formal symbol input as a literal:
Indirect a pointer: Evaluate a symbol to get the formal symbol we Really want to inspect
Mechanics
Mechanics
SymbolInspector relies on three metaprogramming features:
1
.SetAttributes[..., HoldAll]
Solution: HoldAll prevents x from evaluating, letting the raw symbol x through for deeper inspection.
2
.OwnValues Introspection
Problem: In Mathematica, variables normally store definitions (if any) in OwnValues, whereas functions (f[x_] := x) store definitions in DownValues.
Solution: To detect whether x is a pointer to another symbol, interrogate OwnValues[x]. If we find a rule like {x :> y}, we know x is a pointer.
3
.“Safe Peek” Pattern
Code: ReleaseHold[Replace[Hold[s], sym_Symbol :> OwnValues[sym], {1}]]
This specific idiom extracts a symbol’s OwnValue without letting the symbol evaluate.
3
.1
.Hold[s] freezes the symbol.
3
.2
.Replace swaps the symbol for its OwnValues rule list inside the hold.
3
.3
.ReleaseHold lets us look at the rule list.
Learning
Learning
Simply calling OwnValues[s] inside a held function often evaluates s too early. The idiom above was the only way we found to get the definition safely.
“Smart Pointer” Logic
“Smart Pointer” Logic
The distinctive feature of this tool is the isPointer check.
Logic: MatchQ[eval, {(_ :> _Symbol)}]
Behavior:
1
.If x is defined as x = y where y is a symbol, the Inspector detects this pattern.
2
.It “dereferences” the pointer, switching focus to y, ensuring that inspecting a variable holding a formal symbol yields the properties of the formal symbol content, not the properties of the “pointer” variable (the container).
Case Analysis
Case Analysis
We can pass in
◼
naked symbols, naked strings,
◼
naked symbols and strings in variables
◼
construction expressions for formal symbols (with Evaluate!),
◼
constructed formal symbols from symbols or string in variables.
Considering both Pure (non-extended) and Extended symbols, that makes 32 cases, plus a few corner cases. We don’t cover all the cases in the examples below, but we do point out some recommended usages and some delicate points.
Naked Symbols and Strings
Naked Symbols and Strings
DO THIS: Note the System namespace; Pure formals live in System`. Also note that the symbol is automatically protected by Wolfram and has no OwnValue.
DON’T DO THIS: Expect loud failure on strings.
DON’T DO THIS: You can’t assign to a pure system formal, by Wolfram design:
DON’T DO THIS: You CAN assign values to things that look like naked extended formals, but it is highly discouraged. They’re not extended. They appear in the User classification. The only real extended formals are constructed ones (examples below). There is no such thing as a naked, literal extended formal. The constructor will protect constructed extended formals, as we see below, so you can’t assign values to them after-the-fact.
DON’T DO THIS: Even without values, things that look like literal extended symbols just are not.
Naked Symbols in Variables
Naked Symbols in Variables
DO THIS: Everything above works as expected, just through variables acting, metaphorically, as pointers.
Directly Constructed Symbols
Directly Constructed Symbols
DO THIS: Here is the way to create an extended formal without assigning it to a variable. To inspect it with such direct construction, you must precede the call of CreateExtendedFormal with an Evaluate.
DO THIS: After you do this, the symbol itself, as a literal, now has properties that mark it as a bona-fide extended formal:
DON’T DO THIS: Recall that if it has not been constructed, it’s not an extended formal:
DO THIS: If you construct an extended formal from a pure system formal, it’s ok. It will (idempotently) stay a pure system formal.
DO THIS: You can construct extended formals from strings:
DON’T DO THIS: After construction, you can’t assign to it:
DO THIS: As usual, the extended formal is now defined until you Clear it:
Constructed Symbols in Variables
Constructed Symbols in Variables
Works as expected, from strings or from literals
On Improper Inputs
On Improper Inputs
We will see $Failed’s in some slots, so there’s little chance of missing a user error.
Master Class in Evaluation Control
Master Class in Evaluation Control
Wolfram Mathematica is a term-rewriting system, not a function evaluator like Clojure or JavaScript, let alone a function compiler like C or Java. There are enough similarities between term-rewriting and function-calling that we often speak of “calling a function” in Mathematica, but actually that is the unusual case, as with explicit Function forms or foo[#]& syntax. Most of the time, we’re applying rules as if by ReplaceAll: replacing the mentioning of SymbolInspector[x] with the definition of SymbolInspector after replacing its argument pattern s_Symbol with x.
But wait, we don’t want the value of x to go in, we want the symbol x itself to go in. Mathematica aggressively tries to evaluate (replace) every term, every symbol, at every opportunity. That makes it tricky to write a rule (pseudofunction) like SymbolInspector that tries to inspect a symbol. Holding on to a symbol inside SymbolInspector is like holding on to a slippery fish that will take any opportunity to get out of your hands and back into the lake!
SetAttributes[SymbolInspector, HoldAll]
SetAttributes[SymbolInspector, HoldAll]
HoldAll stops evaluation of arguments before replacing SymbolInspector with its definition.
Without this, if you wrote SymbolInspector[x] and it so happened that x has the value 5, Mathematica would evaluate x before SymbolInspector even starts. The fish slips out of your hands and back into the lake right away. SymbolInspector would receive 5, fail to match s_Symbol, and return $Failed.
But with the HoldAll (or HoldFirst) attribute, Mathematica skips argument-evaluation and binds the variable s in the body of SymbolInspector to the symbol x, not to its value 5.
ownVals = OwnValues[Unevaluated[s]];
ownVals = OwnValues[Unevaluated[s]];
Pass the wriggling fish s to OwnValues without triggering evaluation.
Even though s is held by SymbolInspector, passing s to another function (really a rule) exposes s to the evaluator again. The fish slips away whenever you try to move it. If s is x and x = 5, OwnValues[s] becomes OwnValues[5].
Why not OwnValues[Hold[s]]? OwnValues expects a Symbol argument, not a Hold object.
Unevaluated[s] tells OwnValues: “Pretend I passed this fish wrapped in Hold, but treat it as the raw argument.” Unevaluated evaporates after the rewrite of OwnValues[s] to its definition.
The Evaluation Override: Hold[Evaluate[...]]
The Evaluation Override: Hold[Evaluate[...]]
Task: Perform a calculation within a metaphorical gated box.
Scenario: You have ownVals, a list of rules. You need to extract the target symbol, e.g., x, from that list and wrap it in a Hold so it doesn’t evaluate to 5.
Mechanics:
Hold[...] normally stops everything.
Evaluate[...] exceptionally and idiomatically forces evaluation before the Hold freezes the result.
Result: Hold[Evaluate[ownVals〚1,2〛]] rewrites to Hold[x].
Without Evaluate, you would be holding the code ownVals〚1,2〛 rather than the symbol x.
Trojan Horse: Replace[heldSym, Hold[sym_] :> With[{...}]]
Trojan Horse: Replace[heldSym, Hold[sym_] :> With[{...}]]
Task: move a symbol, without triggering evaluation, from the locked box (Hold) into a working environment (With).
Gotcha: You have heldSym, which is Hold[x]. You can’t just say sym = ReleaseHold[heldSym] because x would instantly evaluate into 5. The fish wriggles out of the box and back into the lake.
The Trick:
◼
Match the pattern Hold[sym_], binding sym to the unevaluated content x inside the hold.
◼
(RuleDelayed) inserts that raw symbol x into the body of With.
◼
This injection happens structurally, bypassing the evaluator.
Scope: With[{name = SymbolName[Unevaluated[sym]], ...}, ...]
Scope: With[{name = SymbolName[Unevaluated[sym]], ...}, ...]
Task: Calculate properties once and freeze them as constants.
Why: Inside the With body, sym is the raw symbol x. If we used sym directly in the grid without Unevaluated, it would evaluate to 5. Another opportunity for the fish to slip away.
Strategy: Construct safe strings (name, ctx, attr) in the With header (using Unevaluated shields). In the With body, every instance of name in the Grid is replaced with the string “x”. Strings are safe; they don’t evaluate.
The Display Wrapper: HoldForm[s]
The Display Wrapper: HoldForm[s]
Task: “Take a snapshot” of the symbol for display purposes.
Difference from Hold: Hold[s] evaluates to Hold[x]. HoldForm[s] evaluates to x (visually), but it is an inert form, protected from further evaluation. It is purely cosmetic.
Summary Table
Summary Table
CITE THIS NOTEBOOK
CITE THIS NOTEBOOK
Formal symbols in calculus: evaluation control
by Brian Beckman
Wolfram Community, STAFF PICKS, April 14, 2026
https://community.wolfram.com/groups/-/m/t/3686348
by Brian Beckman
Wolfram Community, STAFF PICKS, April 14, 2026
https://community.wolfram.com/groups/-/m/t/3686348