The LocalPatterns package
By Ted Ersek
      ersektr@navair.navy.mil

One must be careful about using (lhs=rhs), (lhs->rhs), (lhs^=rhs), or (f/:lhs=rhs) if a named pattern is in (lhs) and the pattern name is needed in (rhs).  Named patterns can include (x_), (x__), (opts___), (m_:4) (j_Integer), (t_?Positive), (n_.) and many other forms.  Methods of avoiding problems with (lhs = rhs), (lhs -> rhs), (lhs ^= rhs), (f /: lhs = rhs) are presented below.  One method of avoiding problems is to use functions RightArrow, DotEqual, HumpEqual, and LocalTagSet defined in the (LocalPatterns.m) package.  Each of these functions are demonstrated below, but first you should put the LocalPatterns.m  package in the \(TraditionalForm\`\(\(\\)\(AddOns\)\)\ExtraPackages\)  folder and evaluate the next cell.

<< LocalPatterns.m

The BasicInput2 palette

The LocalPatterns package uses the special symbols (⟶  ≐   ≏  | ).  To assist with entering these symbols the BasicInput palette was modified to include a new row with buttons for these symbols.  Below the row of buttons for \(TraditionalForm\`\((\(\(==\)\(\ \ \)\(≠\)\(\ \ \)\(≤\)\(\ \ \)\(≥\)\)\(\ \ \)\(∈\))\)\)  you will find a row of buttons for \(TraditionalForm\`\((\(\(⟶\)\(\ \ \)\(\(≐\)\(\ \ \)\(≏\)\)\)\ \  | \ \  => )\)\).  You can also move the mouse over many of the buttons in the BasicInput2 palette and the status area at the bottom of the palette will show the alias for the symbol the button would produce.  Hence the palette can be used as a quick reference for the aliases of many symbols.  Keep in mind a number of symbols have more than one alias, but I programmed the palette to show only one alias for each symbol.  For example the status area shows that the alias for  η  is EscetaEsc, but EscetEsc and EschEsc are valid aliases for the same symbol.  It may be that you prefer a different alias than the one I decided to display, and you can modify the source code for the BasicInput2 palette with a text editor if you like.  If you store the BasicInput2 palette in the folder /SystemFiles/FrontEnd/Palettes you will be able to access it with the menu selection (File ⊳ Palettes).  If you are sure you like the BasicInput2 palette better than the BasicInput palette, you can replace the original palette with the modified palette.

Set and DotEqual with named patterns

In the next cell (=) has (x_) on the left and (x) on the right.  This attempt to define the function doesn't work because the global value of (x) is substituted in the integral before the integral is evaluated.  You could use delayed assignment (:=) instead, but that would be very inefficient because the symbolic integral would be derived each time (f) is evaluated.

ClearAll[f]; x = 53.34;
f[x_] = Integrate[Log[(Sqrt[x]) + 1], x]

In practice you might assign a value to (x) early in a Mathematica session and forget about it.  A practical solution to this problem is to clear any variables that might cause problems as in the next cell.  This obviously requires a little thought in that the variables used for pattern names shouldn't have values that you care about.

Clear[x];
f[x_] = Integrate[Log[(Sqrt[x]) + 1], x];

?f

Instead of using the solution above you can load the package (LocalPatterns.m) and use the function DotEqual which gives the desired result without clearing the values of any variables.  The cells below show the usage message of DotEqual, and demonstrate that it stores the intended definition for (f) without clearing the global value of x.

?DotEqual

Clear[f];
x = 53.54;
f[x_] ≐ Integrate[Log[(Sqrt[x]) + 1], x]

?f

x

Rule and LongRightArrow with named patterns

The next cell shows an attempt to define a replacement rule called (g2).  In this case (->) has (x_) on the left and (x) on the right, and the rule doesn't work at all because the global value of (x) is substituted on the right side of (->) before the integral evaluates.  As we saw in the last problem the substitution is made before the integral evaluates, and Integrate gets arguments it can't work with.  You could use RuleDelayed (:>) instead but that would be inefficient because the symbolic integral would evaluate each time the rule is used.

x = 4;
g2 = g[x_] -> Integrate[Log[(Sqrt[x]) + 1], x];

Exp[2 a] g[a + 2] /. g2

Here again you may have assigned a value to (x) early in the Mathematica session and forgot about it.  You can get the desired result by clearing any variables that might cause problems as in the next cell.  Of course the variables used for pattern names shouldn't have values that you care about.

Clear[x];
g2 = g[x_] -> Integrate[Log[(Sqrt[x]) + 1], x];

Exp[2 a] g[a + 2] /. g2

Instead of using the solution above you can load the package (LocalPatterns.m) and use the function LongRightArrow which gives the desired result without clearing the values of any variables.  The cells below show the usage message of LongRightArrow, and demonstrate that it makes the right replacement without clearing the value of x.

?LongRightArrow

x = 34/7;
g2 = g[x_]⟶Integrate[Log[(Sqrt[x]) + 1], x];

Exp[3 a] g[a + 2] /. g2

x

UpSet and HumpEqual with named patterns

The cells below add definitions to Sinh and Cosh, so they should be unprotected using the next cell before proceeding.

Unprotect[Sinh, Cosh];

In the next cell (^=) has (x_) on the left and (x) on the right.  This doesn't give the intended result because the global value of (x) is substituted on the right side of (^=) before the series expansion is derived.  You could use UpSetDelayed (^:=) instead but then the series expansion will be derived each time the UpValue is used.

Clear[Sinh, Cosh];
x = 4;
Sinh[x_] + Cosh[x_] ^= 
        Normal[Series[Exp[x], {x, 0, 7}]];

The attempted definition in the previous cell is stored in UpValues[Sinh] and UpValues[Cosh].  Below we see the wrong definition was stored.

UpValues[Sinh]

UpValues[Cosh]

Once again you may have assigned a value to (x) early in the Mathematica session and forgot about it.  As with use of (=) and (->) you can get the desired result by clearing any variables that might cause problems as in the next cell.  As before the variables used for pattern names shouldn't have values that you care about.

Clear[x, y];
Sinh[x_] + Cosh[x_] ^= 
        Normal[Series[Exp[x], {x, 0, 7}]];

UpValues[Sinh]

UpValues[Cosh]

Instead of using the solution above you can load the package (LocalPatterns.m) and use the function HumpEqual which gives the desired result without clearing the values of any variables.  The cells below show the usage message of HumpEqual, and demonstrate that it stores the right definition without clearing the value of x.

?HumpEqual

Clear[Sinh, Cosh];
x = 4;
Sinh[x_] + Cosh[x_] ≏ 
        Normal[Series[Exp[x], {x, 0, 7}]];

UpValues[Sinh]

UpValues[Cosh]

x

Sinh[z + 7] + Cosh[z + 7]

Evaluating the next cell will remove the definitions above.

Clear[Sinh, Cosh]
Protect[Sinh, Cosh];

TagSet and LocalTagSet with named patterns on the left side

The cells below will add a definition to Sinh, so it should be unprotected using the next cell before proceeding.

Unprotect[Sinh];

In the next cell (=) has (x_) is on the left and (x) on the right.  This attempt to make a definition for Sinh doesn't give the intended result because the global value of (x) is substituted on the right side of (=) before the series expansion is derived. You could use TagSetDelayed (f /: lhs := rhs) instead, but then the series expansion will be derived each time the UpValue is used.

Clear[Sinh];
x = 4;
Sinh /: Sinh[x_] + Cosh[x_] = 
        Normal[Series[Exp[x], {x, 0, 7}]];

The attempted definition in the previous cell is stored in UpValues[Sinh].  Below we see the wrong definition was stored.  Recall the cell above stores a definition in UpValues[Sinh] but not in UpValues[Cosh].

UpValues[Sinh]

Once again you may have assigned a value to (x) early in the Mathematica session and forgot about it.  As with use of (=) and (->) you can get the desired result by clearing any variables that might cause problems as demonstrated in the next cell.  As before the variables used for pattern names shouldn't have any values that you care about.

Clear[x];
Sinh /: Sinh[x_] + Cosh[x_] = 
        Normal[Series[Exp[x], {x, 0, 8}]];

In the next cell we see that the intended definition is stored in UpValues[Sinh].

UpValues[Sinh]

Instead of using the solution above you can load the package (LocalPatterns.m) and use the function LocalTagSet which gives the desired result without clearing the values of any variables.  The cells below show the usage message of LocalTagSet and demonstrate that it stores the right definition without clearing the value of x.

?LocalTagSet

Clear[Sinh];
x = 4;
Sinh \[VerticalSeparator] 
        Sinh[x_] + Cosh[x_] ≏ 
          Normal[Series[Exp[x], {x, 0, 7}]];

UpValues[Sinh]

x

Sinh[z + 7] + Cosh[z + 7]

Evaluating the next cell will remove the definitions above.

Clear[Sinh]
Protect[Sinh];

A function called PatternNames

PatternsNames is a utility function included in the LocalPatterns.m  package.  The definitions of DotEqual, LongRightArrow, HumpEqual, LocalTagSet call on PatternNames, and it might prove useful in other situations.  The usage message below explains the PatternNames function.

?PatternNames

Early versions of the LocalPatterns.m  package didn't include a separate function to do what PatternNames does.  Robby Villegas (of Wolfram Research) recommended use of a separate function and provided an elegant implementation.  The code Robby Villagas provided was modified slightly to ensure Hold[{}] is returned when no named patterns are present.  This change wasn't necessary, but was made to ensure the result always has the same form.  

An example is given in the next cell where the pattern names include (x, y).  Notice the output doesn't include (a,b) and  (x,y) don't evaluate to (6,3).

x = 6;
y = 3;
PatternNames[Sin[a + b + x_ + y_]]

In the next cell PatternNames returns the symbols used for other patterns.

PatternNames[f[x_Rational, y_?Positive]]

In the next cell PatternNames returns the symbols used for more complicated patterns.

mtrx = {{2, 0}, {0, -1}};
n = 23;
PatternNames[f_[mtrx : {{__Real} .. },  n_Integer:  4]]

The patterns in the next example aren't named, and PatternNames returns an empty list wrapped in Hold.

PatternNames[f[_?Positive, _Integer]]