我如何处理多级缩进?

时间:2015-10-08 03:05:29

标签: haskell indentation code-organization

我正在编写一个具有非常复杂的循环的脚本:

main = do
    inFH <- openFile "..." ReadMode
    outFH <- openFile "..." WriteMode

    forM myList $ \ item ->
        ...
        if ... 
            then ...
            else do
                ...
                case ... of
                    Nothing -> ...
                    Just x  -> do
                        ...
                            ...

代码很快就会飞到右边,所以我想把它分成几块,使用例如where子句。问题是,其中许多...包含对两个句柄inFHoutFH的读/写语句,并且使用where语句将使这两个名称脱离上下文。每次我使用where语句时,我都必须发送这两个变量。

有没有更好的方法来解决这个问题?

1 个答案:

答案 0 :(得分:9)

在许多情况下,这些深度嵌套的缩进是深度嵌套错误检查的结果。如果是这样的话,你应该调查MaybeT及其大哥ExceptT。这些代码提供了一种干净的方法来将“我们做什么时出错”的代码与“假设一切正常我们做什么”代码分开。在您的示例中,我可能会写:

data CustomError = IfCheckFailed | MaybeCheckFailed

main = handleErrors <=< runExceptT $ do
    inFH  <- liftIO $ openFile ...
    outFH <- liftIO $ openFile ...
    forM myList $ \item -> do
        when (...) (throwError IfCheckFailed)
        ...
        x <- liftMaybe MaybeCheckFailed ...
        ...

liftMaybe :: MonadError e m => e -> Maybe a -> m a
liftMaybe err = maybe (throwError err) return

handleErrors :: Either CustomError a -> IO a
handleErrors (Left err) = case err of
    IfCheckFailed    -> ...
    MaybeCheckFailed -> ...
handleErrors (Right success) = return success

请注意,我们仍会在forM循环中增加缩进;但其他检查在main中“在线”完成,并在handleErrors中的所有缩进级别处理。