web-dev-qa-db-ja.com

Haskell-caseステートメント内のガード

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ブロックを変換して、次のようなステートメントをガードしたかったのです。

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 Ex1SO Ex2 ガードを使用できる場所を示す明確なソースはありますか?

18
skgbanga

ガードが許可される場所は2つあります。関数定義とcase式です。両方のコンテキストで、ガードはafterパターンとbefore本体、したがって、=関数および->caseブランチ、通常どおり:

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

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

ガードはパターンの単純な制約なので、Just xNothing以外の値に一致しますが、Just x | x > 0は、ラップされた値も正であるJustとのみ一致します。

最終的な参照は、 Haskell Report 、具体的には§3.13Case Expressionsおよび§4.4.3Function 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
45
Jon Purdy