# How to set up new types for pattern matching strings?

Consider the following toy example: I have a set of language sounds, which I partition into two exclusive subsets, consonants and vowels. I want to set up string patterns for e.g. StringMatchQ that may contain restrictions based on the sound specification. For sake of simplicity, I used letters instead of sounds, as for now it does not matter.

(* Define domains *)
vowels = {"a", "e", "i", "o", "u"};
consonants = {"b", "c", "d", "f", "g"};

(* Define predicates *)
VowelQ[x_] := MemberQ[vowels, x];
ConsonantQ[x_] := MemberQ[consonants, x];

(* Use predicates in pattern matcher *)
Shortest[pre__] ~~ c : __?ConsonantQ ~~ v_?VowelQ ~~ EndOfString :>
pre <> "-[" <> c <> "]-[" <> v <> "]"]


"ba-[dg]-[e]"

My question is:

How to set up domains that can be used like e.g. DigitCharacter in the pattern matcher,

that is, how to define Consonant and Vowel in the following (putative) application to yield the same result as the above code?

StringReplace["badge",
Shortest[pre__] ~~ c : Consonant .. ~~ v : Vowel ~~ EndOfString :>
pre <> "-[" <> c <> "]-[" <> v <> "]"]


I can define new data structures, like Consonant["c"], that is displayed as "c", but is nevertheless interpreted as Consonant["c"], though I have the feeling that this is not the way to do it.

-

I suggest to use lexical environments, in particular the function makeCustomEnvironment which I posted in this answer

ClearAll[makeCustomEnvironment];
SetAttributes[makeCustomEnvironment, HoldAll];
makeCustomEnvironment[values : (_Symbol = _) ..] :=
Function[code, With @@ Hold[{values}, code], HoldAll];


now, define a custom environment:

env = makeCustomEnvironment[Consonant = _?ConsonantQ, Vowel = _?VowelQ]


And use it:

env@StringReplace["badge",
Shortest[pre__]~~c:Consonant..~~v:Vowel~~EndOfString:>pre<>"-["<>c<>"]-["<>v<>"]"]

(*
==> "ba-[dg]-[e]"
*)


Note that the literals Consonant and Vowel don't acquire any global values, and have special meaning only for the code literally present inside the env environment. You can view this solution as a simple example of Mathematica meta-programming.

EDIT

Another alternative (which I like less, but still) is to use UpValues:

ClearAll[Consonant, Vowel]
Consonant /: f_[left___, Consonant, right___] :=
f[left, _?ConsonantQ, right];
Vowel /: f_[left___, Vowel, right___] := f[left, _?VowelQ, right];


In this case, you can run your code without any wrappers, but I personally would still go for local environments (note that I defined the above pretty carelessly for any f - for example, you won't be able to Clear Consonant or Vowel easily now. If you choose this method, you may want to narrow the set of f-s in the above).

And of course, you can just simply use assignments

ClearAll[Consonant, Vowel]
Consonant = _?ConsonantQ;
Vowel     = _?VowelQ;


but this will likely exclude the use of symbols Consonant and Vowel in other capacities in some other pieces of your code, since they now have global values. This may also not work if they are used inside functions which hold their arguments, since they are replaced by their values as a result of their evaluation, in this method.

-
Very useful, as usual! Now since I want to package the whole Consonant/Vowel thing with a lot more, it seems reasonable to use UpValues instead of the environment wrapper, for deployment. What are the drawbacks of UpValues compared to makeCustomEnvironment? –  István Zachar Mar 6 '12 at 12:02
@Istvan The main drawback is that UpValues create global effects (although localized in certain sense, but still), so they may fire in some unforeseen cases. This will be less of an issue if you put your Consonant and Vowel into some package (namespace). But, you may have some unexpected behavior in your own code, like e.g. above, where you can't easily clear them. If you package this stuff with a lot more, you probably can use env (local environments) internally, hiding them behind some interface function, so the user won't see them (and I'd think this is cleaner than UpValues). –  Leonid Shifrin Mar 6 '12 at 12:12