这是关于编程风格和常见实践的更多问题.但我觉得它不适合代码审查论坛......
我的程序解析正则表达式并处理它们.正则表达式可以包含常用元素(Kleene闭包,连接等),它也可以通过名称引用其他正则表达式,如宏:
data Regex a = Epsilon
| Literal a
| Ranges [(a, a)]
| Ref String
| Then (Regex a) (Regex a)
| Or (Regex a) (Regex a)
| Star (Regex a)
Run Code Online (Sandbox Code Playgroud)
我处理正则表达式,并解决所有宏引用,并转换后Literal的元素Range元素(这是需要我的目的),我最终能够也不应该有型Ref和Literal,所以在我的功能与它的工作我做就像是:
foo (Literal _) = error "unexpected literal"
foo (Ref _) = error "unexpected reference"
foo (Epsilon) = ...
foo (Star x) = ...
...
Run Code Online (Sandbox Code Playgroud)
这看起来很难看,因为它在编译期间执行运行时检查而不是检查.不是一种非常类似的方法.
那么也许我可以引入另一种与原始数据非常相似的数据类型并使用它?
data RegexSimple a = Epsilon2
| Ranges2 [(a, a)]
| Then2 (Regex a) (Regex a)
| Or2 (Regex a) (Regex a)
| Star2 (Regex a)
Run Code Online (Sandbox Code Playgroud)
这样可行,但在这里我有很多重复,而且现在我还需要创建新的构造函数,并且我需要创建新的...
专家会在这做什么?我想学习 : )
我不知道你的代码的其余部分是什么样的,所以这个解决方案可能需要你重新考虑某些方面,但对这个问题最"解决问题"的方法可能是使用GADT和幻像类型.它们一起基本上允许您创建任意子类型以实现更灵活的类型安全性.你会像这样重新定义你的类型.
{-# LANGUAGE GADTs #-}
data Literal
data Ref
data Rangeable
data Regex t a where
Epsilon :: Regex Rangeable a
Literal :: a -> Regex Literal a
Ranges :: [(a, a)] -> Regex Rangeable a
Ref :: String -> Regex Ref a
Then :: Regex t' a -> Regex t' a -> Regex Rangeable a
Or :: Regex t' a -> Regex t' a -> Regex Rangeable a
Star :: Regex t' a -> Regex Rangeable
Run Code Online (Sandbox Code Playgroud)
然后你可以定义
foo :: Regex Rangeable a
foo (Epsilon) = ...
foo s@(Star a) = ...
Run Code Online (Sandbox Code Playgroud)
现在,类似的语句foo $ Literal 'c'将无法进行编译时类型检查.