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 ガードを使用できる場所を示す明確なソースはありますか?
ガードが許可される場所は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 x
はNothing
以外の値に一致しますが、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