let*_*aik 2 haskell pattern-matching generic-programming gadt
data Foo = Bar1
| Bar2 Foo Foo
| Bar3 Foo
| Bar4 Foo Foo Foo
Run Code Online (Sandbox Code Playgroud)
现在,假设有人构建了一个Foo
树,我想检查Foo值的参数是否有效.构造函数参数的规则是:
Bar2 Bar1 Foo
Bar3 (Bar2|Bar3|Bar4)
Bar4 Bar1 Bar1 (Bar1|Bar4)
我知道值的构造函数,只想检查立即参数,没有任何递归.像这样:
bar2 Bar1 Bar1 = True
bar2 Bar1 (Bar2 {}) = True
bar2 Bar1 (Bar3 _) = True
bar2 Bar1 (Bar4 {}) = True
bar2 _ _ = False
Run Code Online (Sandbox Code Playgroud)
例如Bar4:
bar4 Bar1 Bar1 Bar1 = True
bar4 Bar1 Bar1 (Bar4 {}) = True
bar4 _ _ _ = False
Run Code Online (Sandbox Code Playgroud)
我怎样才能最简洁地表达这些条件?列出所有组合在某些情况下有点太多了.据我所知,不存在用于模式匹配的"OR"-syntax.
UPDATE
我改编了Daniel的解决方案并且已经解决了这个问题:
data Foo = Bar1
| Bar2 Foo Foo
| Bar3 Foo
| Bar4 Foo Foo Foo
deriving (Data, Typeable)
bar2 a b = a `isOf` [Bar1] && b `isOf` [Bar1,Bar2{},Bar3{},Bar4{}]
bar4 a b c = [a,b] `areOf` [Bar1] && c `isOf` [Bar1,Bar4{}]
isOf l r = toConstr l `elem` map toConstr r
areOf l r = all (`isOf` r) l
Run Code Online (Sandbox Code Playgroud)
我喜欢这个是我不必更改我的数据类型,除了添加deriving子句,并且它是可读的.当然缺点是这些都是动态检查.在我的情况下,这很好,因为它只是用于类似断言的检查来发现编程错误.
我认为如何在类型系统级别上检查它的唯一方法是将其拆分为多种数据类型.就像是:
data Foo = Foo1 Bar1 | Foo2 Bar2 | Foo3 Bar3 | Foo4 Bar4
data Bar1 = Bar1
data Bar2 = Bar2a Bar1 Foo
data Bar3 = Bar3a Bar2 | Bar3b Bar3 | Bar3c Bar4
data Bar4 = Bar4a Bar1 Bar1 Bar1 | Bar4b Bar1 Bar1 Bar4
Run Code Online (Sandbox Code Playgroud)
您不能说要将某些类型变量限制为某些构造函数,因此需要为此创建新的数据类型.
缺点是你需要输入更多的构造函数/模式,但这可以通过一些辅助函数至少部分解决.
编辑:也许另一种解决方案是使用幻像类型来使用GADT标记构造函数:
{-# LANGUAGE GADTs #-}
data FBar1
data FBar2
data FBar3
data FBar4
data Foo a where
Bar1 :: Foo FBar1
Bar2 :: Foo FBar1 -> Foo b -> Foo FBar2
Bar3a :: Foo FBar2 -> Foo FBar3
Bar3b :: Foo FBar3 -> Foo FBar3
Bar3c :: Foo FBar4 -> Foo FBar3
Bar4a :: Foo FBar1 -> Foo FBar1 -> Foo FBar4
Bar4b :: Foo FBar1 -> Foo FBar4 -> Foo FBar4
Run Code Online (Sandbox Code Playgroud)
我不确定这个解决方案是否会产生比解决方案更多的问题.例如,不可能编写这样的函数:
construct :: Int -> FooAny X
construct 0 = Bar1
construct 1 = Bar2 Bar1 Bar1
Run Code Online (Sandbox Code Playgroud)
因为X
必须是FBar1
和FBar2
.你需要存在的东西,例如把它包装成
data FooAny where
FooAny :: Foo a -> FooAny
construct :: Int -> FooAny
construct 0 = FooAny $ Bar1
construct 1 = FooAny $ Bar2 Bar1 Bar1
Run Code Online (Sandbox Code Playgroud)