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

ガードが許可される場所は 2 つあります。関数定義と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 > 0a にのみ一致します。Just

決定的なリファレンスは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
于 2016-11-28T02:46:37.717 に答える