This is more a question about programming style and common practices. But I feel like it doesn't fit into the code review forum ...
My program parses regular expressions and processes them. A regular expression can have regular elements (Kleene closure, concatenation, etc.), as well as links to other regular expressions by their names, for example macros:
data Regex a = Epsilon | Literal a | Ranges [(a, a)] | Ref String | Then (Regex a) (Regex a) | Or (Regex a) (Regex a) | Star (Regex a)
After processing the regular expression and resolving all macro references and converting Literal elements to Range elements (this is necessary for my purposes), I get a type that Ref and Literal cannot and should not, therefore, in my functions that work with it, I am doing something like:
foo (Literal _) = error "unexpected literal" foo (Ref _) = error "unexpected reference" foo (Epsilon) = ... foo (Star x) = ... ...
This looks ugly to me because it checks runtimes instead of checks at compile time. Not a good approach.
So maybe I can introduce another data type that is very similar to the original one and use?
data RegexSimple a = Epsilon2 | Ranges2 [(a, a)] | Then2 (Regex a) (Regex a) | Or2 (Regex a) (Regex a) | Star2 (Regex a)
It will work, but here I have a lot of duplication, and now there are beautiful and descriptive names of constructors, and I need to invent new ones ...
What could experts do? I wanna know:)
source share