与monadic后卫的详尽案件

时间:2016-07-18 17:46:36

标签: haskell pattern-matching monads

我的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进行详尽的检查?

4 个答案:

答案 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
       ....

根据您的实际情况,您可能希望使用不同的中间类型,甚至可以创建自己的自定义类型,或者意识到您实际上甚至不需要任何类型。