我的case
表达式具有相对大量的模式:
case x of
... -> ...
... -> ...
... -> ...
... -> ...
...
_ -> ...
其中一个案件有警卫:
case x of
... -> ...
... -> ...
... | condition -> ...
-- If condition is false, fall through to “Rest”.
-- Rest:
... -> ...
... -> ...
...
_ -> ...
如果后卫不匹配,我们只是落到剩下的情况,没问题。但现在我需要单独测试条件,所以我这样做:
case x of
... -> ...
... -> ...
... -> do
condition <- action
if condition
then ...
else ... -- How to fall through?
-- Rest:
... -> ...
... -> ...
...
_ -> ...
然而,我想我已经犯了一个错误。似乎没有办法让else
分支继续处理其余的情况而不重复这些分支或将它们分解为函数。无论哪种方式都会因为穷举检查而混乱:如果我想在守卫之后添加一个案例,编译器就不知道这些匹配是否是详尽的。
如何更改此功能,或参数化/包装数据类型,以使用monadic guard进行详尽的检查?
答案 0 :(得分:3)
一个简单的选择是获取case
块的后半部分并将其放在单独的函数中。
case (x, y) of
(Foo ..., Foo ...) -> ...
x@(Bar ..., Bar ...) -> do
condition <- action
if condition
then ...
else rest x
x -> rest x
rest (Baz ..., ...) = ...
rest (Var ..., ...) = ...
...
rest _ = undefined
在undefined
的漏洞案例中使用rest
来捕捉您认为应该在原始case
块的前半部分匹配的模式,这有点令人不满意。如果您设法违反前提条件((Foo, Foo)
等不匹配),那么您将收到运行时错误。
答案 1 :(得分:3)
我不喜欢下面的方法,但无论如何我都会分享它:
fix (\proceed b -> case (x, y, b) of
(Foo ..., Foo ..., False) -> ...
(Bar ..., Bar ..., False) -> do
condition <- action
if condition
then ...
else proceed True
(Baz ..., ..., _) -> ...
(Var ..., ..., _) -> ...
...
) False
附加标志b
最初为false,因此会考虑所有分支。一旦我们proceed
,我们将其设置为true,以便跳过第一个分支。
根据实际模式,可能会或可能不会静态地找到详尽的信息。
答案 2 :(得分:0)
我最终做的是使用配备Maybe
的monad堆栈,并将case
替换为asum
一系列操作,遵循以下结构:
asum
[ do
pattern1 <- pure input -- failed pattern matches fall through
body1
, do
pattern2 <- pure input
body2
, do
pattern1 <- pure input
guard condition -- guards also fall through
body3
, ...
]
正如我最近在this answer中向其他人提出的问题所述。不幸的是,这不允许进行详尽的检查。
答案 3 :(得分:0)
显而易见的问题是:你能不解开纯粹而不纯洁的一点? 没有实际的代码很难说,但如果只有一个条件实际上是问题,你可以使用Either或Maybe来做两个级别的情况,它捕获所有特定的条件。
step <-case x of
pattern2 -> condition <- action
if condition
then Just whatToDo
else Nothing
pattern5 -> condition <- action2
if condition
then Just whatToDo2
_ -> Nothing
case step of
Just action -> action
Nothing -> case x of
pattern 1 -> body1
....
根据您的实际情况,您可能希望使用不同的中间类型,甚至可以创建自己的自定义类型,或者意识到您实际上甚至不需要任何类型。