Haskell - case 语句中的守卫

Haskell - guard inside case statement

我正在阅读 Learn you a haskell 这本书,在第 8 章中有一段代码看起来像这样

data LockerState = Taken | Free deriving (Eq, Show)
type Code = String
type LockerMap = Map.Map Int (LockerState, Code)

lookup' :: Int -> LockerMap -> Either String Code
lookup' num_ map_ =
   case (Map.lookup num_ map_) of
      Nothing -> Left $ "LockerNumber doesn't exist!"
      Just (state, code) -> if state == Taken
                              then Left $ "LockerNumber already taken!"
                              else Right $ code

这行得通。但是,我想将 if/else 块转换为 guard 语句,如下所示:

lookup' :: Int -> LockerMap -> Either String Code
lookup' num_ map_ =
   case (Map.lookup num_ map_) of
      Nothing -> Left $ "LockerNumber doesn't exist!"
      Just (state, code) ->
         | state == Taken = Left $ "LockerNumber already taken!"
         | otherwise = Right $ Code

这无法编译。 Haskell 中守卫的使用似乎非常 restrictive/non 直观。 SO Ex2。是否有我可以阅读的明确来源告诉我可以在哪些地方使用警卫?

有两个地方允许使用守卫:函数定义和 case 表达式。在这两种情况下,守卫出现在 after 模式和 before the body,所以你在函数中使用 =->case 个分支中,像往常一样:

divide x y
  | y == 0 = Nothing
  --------
  | otherwise = Just (x / y)
  -----------

positively mx = case mx of
  Just x | x > 0 -> Just x
         -------
  _ -> Nothing

Guards 只是 constraints 模式,所以 Just x 匹配任何非 Nothing 值,但 Just x | x > 0 只匹配 Just which wrapped value 也是正数。

我想权威参考是 Haskell Report,特别是 §3.13 Case Expressions 和 §4.4.3 Function and Pattern Bindings,它们描述了守卫的语法并指定了它们的允许位置。

在您的代码中,您需要:

Just (state, code)
  | state == Taken -> Left "LockerNumber already taken!"
  | otherwise -> Right code

这也可以单独用模式来表达:

Just (Taken, _) -> Left "LockerNumber already taken!"
Just (_, code) -> Right code