在 IO 中处理谓词的惯用 Haskell 方式是什么?

Ulr*_*ter 4 haskell predicate conditional-statements io-monad

对于某些文件操作,我需要检查文件是否存在,是否已被修改,然后才对其进行一些操作。我的新手 Haskell 代码如下(简化):

someFileOp ::FileContents -> FilePath -> IO (FileOpResult)
someFileOp contents absFilePath = do
    fileExists <- DIR.doesFileExist absFilePath
    if fileExists
        then do
            isMod <- isModified contents absFilePath
            if isMod
                then return FileModified
            else return $ doSomethingWithFile
        else return FileNotFound
Run Code Online (Sandbox Code Playgroud)

它确实有效。但是,嵌套的 if 表达式在我看来是错误的- 不像 FP。检查 IO 中的几个布尔条件然后根据它们的结果采取一些行动的惯用方法是什么?

Tho*_*son 5

忽略 Daniel 在比赛中的优点以及为什么通常不检查文件的原因,更多的 Haskell 解决方案通常是一个 monad 转换器。这是一个典型的例子,其中 ExtT 变压器有意义。我还包括了 ContT 的(错误)使用,以防您好奇并想探索:

import System.Directory as DIR
import Control.Monad.Trans.Cont
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Except

isModified :: a -> b -> IO Bool
isModified _ _ = pure False

type FileOpResult = Either String String

someFileOp_cont :: String -> FilePath -> IO FileOpResult
someFileOp_cont contents absFilePath = evalContT $ callCC $ \exit -> do
    fileExists <- liftIO $ DIR.doesFileExist absFilePath
    unless fileExists (exit (Left "FileNotFound"))
    isMod <- liftIO $ isModified contents absFilePath
    when isMod (exit (Left "FileModified"))
    return (Right "doSomethingWithFile")

someFileOp_except :: String -> FilePath -> IO FileOpResult
someFileOp_except contents absFilePath = runExceptT $ do
    fileExists <- liftIO $ DIR.doesFileExist absFilePath
    unless fileExists (throwE "FileNotFound")
    isMod <- liftIO $ isModified contents absFilePath
    when isMod (throwE "FileModified")
    return "doSomethingWithFile"
Run Code Online (Sandbox Code Playgroud)

  • 与其他用途相比,这真的是对“ContT”的误用吗?:) (2认同)

Seb*_*raf 5

我会使用whenM :: Monad m => m Bool -> m () -> m()or ifM :: Monad m => m Bool -> m a -> m a -> m a,例如extra

-- | Like 'when', but where the test can be monadic.
whenM :: Monad m => m Bool -> m () -> m ()
whenM mb mt = mb >>= \b ->
  if b
    then mt
    else return ()

-- | Like @if@, but where the test can be monadic.
ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM mb mt me = mb >>= \b ->
  if b
    then mt
    else me
Run Code Online (Sandbox Code Playgroud)