xzh*_*zhu 4 haskell code-organization indentation
我正在编写一个具有非常复杂的循环的脚本:
main = do
inFH <- openFile "..." ReadMode
outFH <- openFile "..." WriteMode
forM myList $ \ item ->
...
if ...
then ...
else do
...
case ... of
Nothing -> ...
Just x -> do
...
...
Run Code Online (Sandbox Code Playgroud)
代码很快就会飞到右边,所以我想把它分成几块,使用例如where子句.问题是,许多这些...包含读/写语句到两个把手inFH和outFH,使用where的语句将呈现这两个名字断章取义.我每次使用where语句时都必须发送这两个变量.
有没有更好的方法来解决这个问题?
在许多情况下,这些深度嵌套的缩进是深度嵌套错误检查的结果.如果那样对你来说,你应该调查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
Run Code Online (Sandbox Code Playgroud)
请注意,我们仍然在forM循环中增加缩进; 但其他检查是"在线"完成的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
Run Code Online (Sandbox Code Playgroud)