1
$\begingroup$

I have a long StringExpression in a package returned by a function. There are some named parts in the expression that I would like to reference when I use the package.

BeginPackage["FindStuff`"]

BigStringExpr::usage="A big StringExpression with named parts."

Begin["`Private`"]

BigStringExpr[]:= StartOfString ~~ someLetters: ToUpperCase@Alphabet[] .. ~~ "@" ~~ anInteger: (Except["0", DigitCharacter] ~~ DigitCharacter ...) ~~ EndOfString

End[]

EndPackage[]

When I load this package I can get the string expression but the named parts have the complete context path.

<< FindStuff`
BigStringExpr[]
(* StartOfString ~~ 
 FindStuff`Private`someLetters : ({"A", "B", "C", "D", "E", "F", "G", 
     "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", 
     "U", "V", "W", "X", "Y", "Z"} ..) ~~ "@" ~~ 
 FindStuff`Private`anInteger : (Except["0", DigitCharacter] ~~ 
    DigitCharacter ...) ~~ EndOfString *)

To use the named parts from the package I have to specify the complete context path.

StringCases["ADSDG@1234", BigStringExpr[] -> {someLetters, anInteger}]
(* {{someLetters, anInteger}} *)

StringCases["ADSDG@1234", BigStringExpr[] -> {FindStuff`Private`someLetters, FindStuff`Private`anInteger}]
(* {{"ADSDG", 1234}} *)

Is there a way to simply reference someLetters and anInteger from BigStringExpr[] without the full context path for the named parts? I have tried Evaluate@BigStringExpr[] without any luck.

$\endgroup$
  • 1
    $\begingroup$ many of the symbolic Mathematica functions have the same problem, the standard way to solve it is to pass the symbols to be used as variables in a second argument... $\endgroup$ – Albert Retey Aug 18 '15 at 19:22
  • $\begingroup$ @AlbertRetey I tried BigStringExpr[someLetters_Symbol, anInteger_Symbol]:= StartOfString ~~ someLetters: ToUpperCase@Alphabet[] .. ~~ "@" ~~ anInteger: (Except["0", DigitCharacter] ~~ DigitCharacter ...) ~~ EndOfString. It complains that the symbols already appear on the right-hand side. How would you pass in the symbols? May you provide and answer post, please. $\endgroup$ – Edmund Aug 18 '15 at 19:37
  • $\begingroup$ You want something like bigStringExpr[someLetters_Symbol, anInteger_Symbol] := With[{sl = someLetters, int = anInteger }, StartOfString ~~ Pattern[sl, ToUpperCase@Alphabet[] ..] ~~ "@" ~~ Pattern[int, (Except["0", DigitCharacter] ~~ DigitCharacter ...)] ~~ EndOfString], where With serves to full the system and avoid those complaints. $\endgroup$ – Leonid Shifrin Aug 18 '15 at 20:01
  • $\begingroup$ @LeonidShifrin Yes, in the mean time I kept looking and found out that they were warning (not error) messages. I turned them off with Off[RuleDelayed::rhs] in the package just before defining BigStringExpr and then turned them back on with On[RuleDelayed::rhs] immediately after. Thanks for the pass in the symbols idea. Making an answer post? $\endgroup$ – Edmund Aug 18 '15 at 20:08
  • $\begingroup$ Well, the main point was coming from @AlbertRetey, so I'd rather see his answer here - I just gave a small technical hint. $\endgroup$ – Leonid Shifrin Aug 18 '15 at 20:40
1
$\begingroup$

This is very similar to a common problem that many symbolic Mathematica functions share, e.g.:

Solve[2*x-5==15,x]

and as can be seen from the example the standard way to solve that is to pass such symbols which are to be used as formal symbols as extra argument(s). As you guessed in your comment the most direct way to implement that would be something like:

BigStringExpr[someLetters_Symbol, anInteger_Symbol] := 
  StartOfString ~~ someLetters : ToUpperCase@Alphabet[] .. ~~ "@" ~~ 
  anInteger : (Except["0", DigitCharacter] ~~ DigitCharacter ...) ~~ 
  EndOfString

which does work, as can be verified with e.g.:

StringReplace["ABC@123", BigStringExpr[a, b] :> StringJoin[b, "---", a]]

A remaining problem is, as you have noted, that a warning is issued at definition time and the syntax highlighting indicates the two names as a syntax error. That message and syntax error are there for understandable reasons as such definitions often not do what users actually want. But in this case it does exactly why we want, so we can safely ignore these warnings.

You found yourself one way to suppress the message, which of course is to just (temporarily) switch off that message with Off[RuleDelayed::rhs] (and switch back on with On after the definition). Newer versions provide the Quiet function to provide exactly the possibility to "temporarily" switch off messages which is shorter and has the advantage to reset the originial state, no matter whether that was on or off. Leonid has shown you another version which avoids the message in the first place by inserting the symbols with With:

bigStringExpr[someLetters_Symbol, anInteger_Symbol] := 
  With[{sl = someLetters, int = anInteger}, 
    StartOfString ~~ Pattern[sl, ToUpperCase@Alphabet[] ..] ~~ "@" ~~ 
    Pattern[int, (Except["0", DigitCharacter] ~~ DigitCharacter ...)] ~~
    EndOfString]

Actually that just "hides" the potential problem from Mathematica so that neither the syntax checker nor the messaging system actually complain. One could see this as just a trick which might fail when a more sophisticated future version of Mathematica will recognize such patterns as "dangerous" as well. So it probably is even cleaner to temporarily switch off the message. That will also be easier to read as it is clear why that part of the code is there at all, where you might have a hard time to find out what the With was there when revisiting the code (a corresponding comment would then of course make sense).

One other thing I would improve (which is not directly related to the message/syntax highlighting) is to make that definition work even when there are definitions for the symbols you are using. First note that this won't work:

x=1;
BigStringExpr[x, y]

With adding:

SetAttributes[BigStringExpr,HoldAll]

you could make your function work even in that case which will make it somewhat more robust.

So for me the cleanest and most robust way to make such a definition seems to be:

Quiet[
  SetAttributes[bigStringExpr,HoldAll];
  bigStringExpr[someLetters_Symbol, anInteger_Symbol] := (
    StartOfString ~~ someLetters : ToUpperCase@Alphabet[] .. ~~ "@" ~~ 
    anInteger : (Except["0", DigitCharacter] ~~ DigitCharacter ...) ~~ 
    EndOfString
  ),
  RuleDelayed::rhs
]

You'll have to live with the syntax highlighting, but that seems OK for me as the Quiet indicates that someone has noted the problem and decided it was OK to ignore the message (and the highlighting). Depending on the code quality requirements a corresponding comment would make sense...

$\endgroup$
  • $\begingroup$ A very comprehensive answer. I've taken note of and implemented the SetAttribute enhancement. $\endgroup$ – Edmund Aug 19 '15 at 17:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.