避免使用没有monad变换器的case表达式梯形图

时间:2014-02-01 09:02:30

标签: haskell coding-style monad-transformers

Real World Haskell monads are introduced中,通过使用Maybe monad来避免代码在屏幕右侧移动。但是,如果案例表达式包含其他单子格,例如EitherIO,会发生什么?

我知道使用monad变换器可以避免使用梯子,但是它们是不是因为风格问题而过度杀伤?是否有另一种惯用和轻量级的方法来解决这个问题?

更新:以下是一个示例:

stair s = do
    s <- getLine
    case stepMaybe s of
         Nothing -> return ()
         Just s1 -> case stepEither s1 of
                         Left  _  -> return ()
                         Right s2 -> case stepList s2 of
                                          [s3:_] -> case stepMaybe s3 of
                                                         Nothing -> return ()
                                                         Just s4 -> print s4
                                          _      -> return ()

2 个答案:

答案 0 :(得分:10)

使用专为此目的而创建的the errors library。它允许您统一各种失败的计算,以就常见的错误处理机制达成一致。

例如,假设您有两个计算,其中一个使用Maybe失败,另一个使用Either String失败:

safeHead :: [a] -> Maybe a
safeHead as = case as of
    []  -> Nothing
    a:_ -> Just a

safeDivide :: Double -> Double -> Either String Double
safeDivide x y = if y == 0 then Left "Divide by zero" else Right (x / y)

我们可以通过两种方式使这两个函数在相同的错误处理机制上达成一致。第一种方法是通过抑制Either错误将Maybe函数转换为String。这就是hush函数的作用:

-- Provided by the `errors` package
hush :: Either e a -> Maybe a

example1 :: [Double] -> Maybe Double
example1 xs = do
    x <- safeHead xs
    hush (safeDivide 4 x)

或者,如果失败,我们可以使用描述性Maybe错误消息对String计算进行注释。这就是note函数的作用:

-- Also provided by the `errors` package:
note :: e -> Maybe a -> Either e a

example2 :: [Double] -> Either String Double
example2 xs = do
    x <- note "Empty list" (safeHead xs)
    safeDivide 4 x

errors包还包含MaybeTEitherT的转换功能。这使您可以统一所有错误处理机制,以使用您选择的目标monad。

使用您的stair示例,您可以通过让他们就MaybeT达成一致来简化此事:

stair = void $ runMaybeT $ do
    s  <- lift getLine
    s4 <- hoistMaybe $ do
        s1 <-           stepMaybe  s
        s2 <- hush    $ stepEither s1
        s3 <- headMay $ stepList   s2
        stepMaybe  s3
    lift $ print s4

答案 1 :(得分:3)

是的。使用较小的缩进设置)但是,严肃地说,以下内容应该在您的简单示例中完成。

import Prelude hiding (mapM_)
import Data.Foldable
import Data.Maybe

stair = do
  s <- getLine
  mapM_ print $ 
    stepMaybe s >>=
    either (const Nothing) Just . stepEither >>=
    listToMaybe . stepList >>=
    stepMaybe

对于其他情况,mapM, mapM_, forM, forM_, sequence, sequence_Data.Foldable的操作Data.Traversable将是您的唯一工具链。假设您有the missing instances for Either

然而,我仍然建议考虑变形金刚,因为它们几乎是处理不同单子组成的标准方法。