30

我正在通过Learn you a haskell book,在第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 块转换为这样的保护语句:

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 中守卫的使用非常严格/不直观。SO Ex1 SO Ex2。有没有我可以阅读的明确来源告诉我可以在哪些地方使用警卫?

4

1 回答 1

66

有两个地方允许使用守卫:函数定义和case表达式。在这两种情况下,守卫出现模式之后和主体之前,因此您可以像往常一样=在函数和分支中->使用:case

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

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

守卫只是模式的约束,因此Just x匹配任何非Nothing值,但Just x | x > 0只匹配Just包装值也是正数的 a。

我想最终的参考是Haskell 报告,特别是 §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
于 2016-11-28T02:46:37.717 回答