Haskell - охранник внутри дела

Я изучаю книгу на 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 Ex1 SO Ex2. Есть ли определенный источник, который я могу прочитать, в котором говорится, в каких местах я могу использовать охрану?

1 ответ

Решение

Есть два места, где допускается защита: определения функций и 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 чье завернутое значение также положительно.

Я предполагаю, что окончательная ссылка - это отчет Haskell, в частности §3.13 Case выражения и §4.4.3 привязки функций и шаблонов, которые описывают синтаксис охранников и указывают, где они разрешены.

В своем коде вы хотите:

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

Это также можно выразить только с помощью шаблонов:

Just (Taken, _) -> Left "LockerNumber already taken!"
Just (_, code) -> Right code
Другие вопросы по тегам