Advanced Topic: Upvalues

When you define a function or transformation rule in the normal way, you are storing a definition to be applied to the head of that function or rule.  Most of the time this is what you want.  But suppose instead you want to define rules for what happens when you add two or more of this object up. Then the head of the expression is Plus, a built-in symbol. While in theory you can modify most built-in symbols, it is not advisable to do so!  What is required is an Upvalue, which creates a definition for your symbol, not the more general symbols.

An Example: Lag Operator

Clear[𝕃]

𝕃[x_[t_]] := x[t - 1]

𝕃  /: 𝕃*x_[t_] := 
      𝕃[x[t]]

Because upvalues can only apply if the object in question is only “one layer deep” in the expression, and because \(TraditionalForm
      \`𝕃\^4 = \ 
        𝕃×𝕃×𝕃×𝕃\) which could cause infinite iteration, you can’t define upvalues for things like Times[(𝕃^a), (𝕃^b)] := 
             (𝕃^(a + b)).  The FullForm makes this clear:   Times[Power[𝕃,a],Power[𝕃,b]].  Instead, we define an auxiliary variable to handle the “powers”.

𝕃  /: (𝕃^i)_Integer := 
      𝕃[p, i]

(𝕃^2)

𝕃  /: 
      𝕃[p, i_Integer] x_Symbol[t_] := 
      Nest[𝕃, x[t], i]  /;  i ≥ 0

𝕃  /:  
      𝕃[p, a_Integer]*
        𝕃[p, b_Integer] :=  
      𝕃[p, a + b]

𝕃  /:   
      𝕃[p, a_Integer]/𝕃[p, 
          b_Integer] :=  𝕃[p, a - b]

𝕃 /:  
      𝕃[p, a_Integer]^b_Integer :=  
      𝕃[p, a b]

Strictly speaking, you could also use a different way to set upvalues:

𝕃[p, 0] x_[t_] ^:= x[t]

which is called UpSetDelayed, as opposed to the other examples which use TagSetDelayed, but you get the same result and TagSet/TagSetDelayed is easier to read.

Here is the Lag Operator in action.

𝕃 θ[τ]

Times @@ Table[𝕃, {4}]

(𝕃^3) y[t]

𝕃  𝕃 𝓎[t]

(𝕃^4)/(𝕃^4) * z[t]