删除文件(如果存在)

yog*_*oth 29 haskell

在Haskell中这样做的正确方法是什么?

if exists "foo.txt" then delete "foo.txt"
doSomethingElse
Run Code Online (Sandbox Code Playgroud)

到目前为止,我有:

import System.Directory
main = do
        filename <- getFileNameSomehow
        fileExists <- doesFileExist filename
        if fileExists 
             then removeFile filename
             ???
        doSomethingElse
Run Code Online (Sandbox Code Playgroud)

ehi*_*ird 47

你最好删除文件,如果它不存在则简单地恢复:

import Prelude hiding (catch)
import System.Directory
import Control.Exception
import System.IO.Error hiding (catch)

removeIfExists :: FilePath -> IO ()
removeIfExists fileName = removeFile fileName `catch` handleExists
  where handleExists e
          | isDoesNotExistError e = return ()
          | otherwise = throwIO e
Run Code Online (Sandbox Code Playgroud)

这可以避免有人在您的代码之间删除文件的竞争条件,检查它是否存在并删除它.在你的情况下可能无关紧要,但无论如何这都是好的做法.

注意这一import Prelude hiding (catch)行 - 这是因为Prelude包含来自异常处理的旧函数,现在不推荐使用Control.Exception,它也有一个名为的函数catch; 导入行只是简单地隐藏了Prelude catch,而使用了Control.Exception.

但是,这仍然会留下更基本的基本问题:如何编写条件IO

那么,在这种情况下,只需这样就足够了

when fileExists $ removeFile filename
Run Code Online (Sandbox Code Playgroud)

(使用Control.Monad.when).但是,通常在Haskell中查看类型是有用的.

条件的两个分支必须具有相同的类型.所以要填写

if fileExists
    then removeFile filename
    else ???
Run Code Online (Sandbox Code Playgroud)

我们应该看看的类型removeFile filename; 无论如何???,它必须具有相同的类型.

System.Directory.removeFile具有类型FilePath -> IO (),因此removeFile filename具有类型IO ().所以我们想要的是一个IO动作,其结果类型()无效.

好吧,目的return是构造一个没有效果的动作,只返回一个常量值,并且return ()具有正确的类型:( IO ()或更一般地说,(Monad m) => m ()).所以,???return ()(你可以看到我在改进的片断中所使用以上,到一筹莫展的时候,removeFile因为该文件不存在失败).

(顺便说一下,你现在应该可以when借助return ()它来实现;它非常简单:))

不要担心,如果你发现最初很难进入Haskell的方式 - 它会自然地及时到来,当它发生时,它是非常有益的.:)

  • 这是Control.Exception文档的链接:http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Exception.html - 我不能把它放在帖子中,因为我不知道有足够的声誉:/ (4认同)
  • 谢谢,这真的很有帮助! (2认同)
  • 欢迎来到SO!很高兴见到你.你现在已经超过100个代表,因此大多数真正令人恼火的限制都应该消失.:] (2认同)

gsp*_*spr 11

(注意: ehird的答案对于竞争条件提出了一个非常好的观点.在阅读我的答案时应该牢记这一点,忽略了这个问题.请注意问题中提出的命令性伪代码也会遇到同样的问题.)

什么定义了文件名?是在程序中给出,还是由用户提供?在命令式伪代码中,它是程序中的常量字符串.我假设您希望用户通过将其作为程序的第一个命令行参数传递来提供它.

然后我建议这样的事情:

import Control.Monad    
import System.Directory
import System.Environment

doSomethingElse :: IO ()

main = do
  args <- getArgs
  fileExists <- doesFileExist (head args)
  when fileExists (removeFile (head args))
  doSomethingElse
Run Code Online (Sandbox Code Playgroud)

(如您所见,我添加了类型签名doSomethingElse以避免混淆).

我导入System.EnvironmentgetArgs功能.如果有问题的文件只是由一个常量字符串给出(例如在你的命令式伪代码中),只需删除所有的args东西并在任何地方填充常量字符串head args.

Control.Monad导入以获取该when功能.请注意,这个有用的函数不是关键字(如if),而是普通函数.我们来看看它的类型:

when :: Monad m => Bool -> m () -> m ()
Run Code Online (Sandbox Code Playgroud)

你的情况mIO,这样你就可以想到的when是,需要一个函数Bool和一个IO动作,并执行只有当动作BoolTrue.当然你可以用ifs 解决你的问题,但在你的情况下when阅读更清楚.至少我是这么认为的.

附录:如果你像我一开始那样,感觉这when是一种神奇而困难的机器,那么尝试自己定义这个功能是非常有益的.我向你保证,这很简单......

  • 考虑*为什么*定义`when`很简单,以及其他含义是什么也是有益的.命令式代码块是Haskell中的第一类实体,只有少数语言可以匹配. (4认同)