Монадические выражения в условных выражениях - компиляции 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, но есть способы избавиться от них.