Haskell 如何处理多个级别的缩进?

Haskell 如何处理多个级别的缩进?,haskell,indentation,code-organization,Haskell,Indentation,Code Organization,我正在编写一个逻辑上非常复杂的循环脚本: main = do inFH <- openFile "..." ReadMode outFH <- openFile "..." WriteMode forM myList $ \ item -> ... if ... then ... else do ... ca

我正在编写一个逻辑上非常复杂的循环脚本:

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

    forM myList $ \ item ->
        ...
        if ... 
            then ...
            else do
                ...
                case ... of
                    Nothing -> ...
                    Just x  -> do
                        ...
                            ...
main=do
通知。。。
只需x->do
...
...
代码很快就飞到了右边,所以我想用
where
子句将代码分成几部分。问题是,其中许多
..
包含对两个句柄
inFH
outph
的读/写语句,使用
where
语句将使这两个名称脱离上下文。每次使用
where
语句时,我都必须发送这两个变量


有没有更好的方法来处理这个问题?

你应该像对待其他编程语言一样做同样的事情。函数应该易于理解。这通常意味着,如果它很长,则没有太多的控制流,否则将其拆分为单独的函数

所以main可能看起来像:

main = do
    inFH <- openFile ...
    outFH <- openFile ....

    mapM prcoessItem myList
main=do

inFH在许多情况下,这些深嵌套缩进是深嵌套错误检查的结果。如果对你来说是这样的话,你应该调查一下
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
data CustomError=IfCheckFailed |可能被检查失败
主=手柄错误m a
liftMaybe err=可能(投掷者错误)返回
handleErrors::自定义错误a->IO a
handleErrors(左错误)=案例错误
如果检查失败->。。。
可能是检查失败->。。。
handleErrors(右成功)=返回成功

注意,我们仍然在
表单
循环中增加缩进;但是其他检查是在
main
中“串联”完成的,并且在
handleErrors

中以相同的缩进级别处理。虽然可能有更好的方法来解决您的具体问题(参见Daniel Wagner的回答),但您始终可以使用
let
在任意范围内引入新名称。这是一个公认的荒谬演示:

main = do
    inFH <- return "inf"
    outFH <- return "ouf"

    let subAction = do
            if length inFH > 2
                then print "foo"
                else subSubAction

        subSubAction = case outFH of
            [] -> print "bar"
            _ -> print "baz"

    forM [1..10] $ \ item -> do
        print item
        subAction
main=do
inFH打印“条”
_->打印“baz”
表格[1..10]$\item->do
打印项目
子作用

Um,这与规定的不在任何地方通过
inFH
outh
的要求非常不一致。谢谢。这是我看过头了的东西。然而,我不喜欢在这里使用
let
,因为它颠倒了阅读顺序——我首先定义了步骤的一部分,然后是流程的其余部分。我也不喜欢给逻辑流的一部分命名,这也是我想避免使用
where
的另一个原因。