Монадические выражения в условных выражениях - компиляции GHC, отказ от кабала

Я почесал голову на день над этим.

У меня есть несколько функций в моем коде, которые выглядят следующим образом:

function :: IO (Maybe Whatever)
function = do
   monadFun
   yaySomeIO
   status <- maybeItWillFail
   if checkStatus status  -- Did we succeed?
   then monadTime >>= return . Just . processItPurely
   else return Nothing

ghci будет загружать и запускать это в интерактивном режиме без проблем, и ghc скомпилирует его с радостью. Однако, запустив это через кабаль, это дает мне следующее:

myProgram.hs:94:16:
Unexpected semi-colons in conditional:
    if checkStatus status; then monadTime >>= return . Just . processItPurely; else return Nothing

Perhaps you meant to use -XDoAndIfThenElse?

И независимо от этого параметра -XDoAndIfThenElse, я не могу найти его в любом месте любой документации. Почему каббал (или этот ghc к этому моменту?) Кричит на меня, чтобы использовать полуколоны, которые там помещают в первую очередь? Или использует монадические выражения в операторах if-then-else только плохую идею?

Обратите внимание, что cabal не жалуется на это вообще:

case checkStatus status of
   True -> monadTime >>= return . Just . processItPurely
   _    -> return Nothing

... за исключением того, что это уродливо, и я никогда не захочу поместить это в свой код. Может ли кто-нибудь сказать мне, что происходит? Пожалуйста, спасибо заранее.

Ответы

Ответ 1

"Правильный" способ отступов if -expressions в do -блоке - это отступы строк else и then, кроме if, как это.

function = do
   monadFun
   yaySomeIO
   status <- maybeItWillFail
   if checkStatus status  -- Did we succeed?
      then monadTime >>= return . Just . processItPurely
      else return Nothing

Это связано с тем, что строки с одинаковым количеством отступов в блоке do обычно рассматриваются как отдельные инструкции.

Однако есть расширение, называемое DoAndIfThenElse, которое позволит вам написать его так, как вы это делали. Это расширение было стандартным в Haskell 2010, поэтому GHC разрешает его по умолчанию.

Cabal имеет тенденцию требовать от вас более подробного описания этих вещей, поэтому, чтобы использовать его в Cabal, вам нужно либо упомянуть его в вашем файле .cabal, либо добавить {-# LANGUAGE DoAndIfThenElse #-} в начало вашего модуля.

Ответ 2

Это не прямой ответ на ваш вопрос, но вы можете исключить оператор if, воспользовавшись MaybeT. Кроме того, foo >>= return . bar совпадает с bar <$> foo. (<$> от Control.Applicative и совпадает с fmap)

function :: MaybeT IO Whatever
function = do
   lift monadFun
   lift yaySomeIO
   status <- lift maybeItWillFail
   guard (checkStatus status)
   processItPurely <$> lift monadTime

Единственное раздражение - это бесплатное разбрызгивание lift s, но есть способы избавиться от них.