【问题标题】:How do I deal with many levels of indentation?如何处理多个级别的缩进?
【发布时间】:2016-01-05 11:48:33
【问题描述】:

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

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 语句时,我都必须发送这两个变量。

有没有更好的处理方法?

【问题讨论】:

    标签: haskell indentation code-organization


    【解决方案1】:

    虽然可能有更好的方法来解决您的具体问题(参见例如Daniel Wagner's answer),但您始终可以使用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
    

    【讨论】:

    • 谢谢。这是我忽略的一点。但是,我不喜欢在这里使用let 的一件事是因为它颠倒了阅读顺序——我首先定义了步骤的一部分,然后是流程的其余部分。我也不喜欢为逻辑流的一部分命名,这是我想避免使用where 的另一个原因。
    【解决方案2】:

    在许多情况下,这些深度嵌套的缩进是深度嵌套错误检查的结果。如果你是这样,你应该看看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 中以相同的缩进级别处理。

    【讨论】:

      【解决方案3】:

      您应该做与任何其他编程语言相同的事情。函数应该易于理解。这通常意味着如果它很长,则没有很多控制流,否则将其拆分为单独的函数。

      所以 main 可能看起来像:

      main = do
          inFH <- openFile ...
          outFH <- openFile ....
      
          mapM prcoessItem myList
      

      【讨论】:

      • 嗯,这与不通过inFHoutFH 到处传递的规定几乎不兼容。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-11
      • 1970-01-01
      相关资源
      最近更新 更多