在守卫中使用单子布尔值? (模式卫士)

Using a monadic boolean value in a guard? (PatternGuard)

我刚刚发现了 PatternGuards 语言扩展,看起来非常不错。

我处于这样一种情况,我想对一个值进行模式匹配,对该值应用一元布尔函数,并且仅在结果为 False 时才执行某些操作。否则我想执行默认操作(可能不像下面列出的那么简单)。

这可以通过在进行模式匹配后使用 if 来实现,但我宁愿不复制默认操作的代码,或者将其移出到一个单独的函数中(即保留 fall通过守卫的行为)

有没有办法不用 if

{-# LANGUAGE GeneralizedNewtypeDeriving, PatternGuards #-}

import Control.Applicative
import Control.Monad.State

newtype MyM a = MyM (StateT Int (Either String) a)
                deriving ( MonadState Int
                         , Monad, Applicative, Functor
                         )

--------------------------------------------------------------------------------

foo :: Int -> MyM Bool
foo = undefined

bar :: Either a Int -> MyM Int
bar (Right n)
  | ok <- foo n, ok == False = return 42
bar _ = return 0

以上代码给出错误信息

Couldn't match expected type ‘MyM Bool’ with actual type ‘Bool’
In the second argument of ‘(==)’, namely ‘False’
In the expression: ok == False

你要求的是不可能的,而且有充分的理由。假设 foo 修改了状态,但守卫中的条件不匹配。您将如何处理这种状态修改?模式与值是分开的,因此匹配模式不会导致改变结果。 您确实需要在 RHS 中调用 foo 来决定如何处理其输出。

在某些情况下,如果 monad 是您可以匹配的数据类型,您可以执行以下操作

foo :: Int -> Maybe Bool
...

bar :: Either a Int -> MyM Int
bar (Right n)
  | Just False <- foo n = ...

但这只是因为您可以直接匹配 monad 的值。

此外,在某些情况下,扩展 lambda-case and multi-way if-expressions 可以让您的生活更轻松。