Remove file if it exists

前端 未结 2 1740
遇见更好的自我
遇见更好的自我 2021-02-05 02:49

What is the right way of doing this in Haskell?

if exists \"foo.txt\" then delete \"foo.txt\"
doSomethingElse

So far I have:

im         


        
相关标签:
2条回答
  • 2021-02-05 03:22

    You would be better off removing the file and simply recovering if it does not exist:

    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
    

    This avoids the race condition of someone deleting the file between your code checking whether it exists and deletes it. It might not matter in your case, but it's good practice anyway.

    Note the import Prelude hiding (catch) line — this is because the Prelude contains older functions from exception handling which are now deprecated in favour of Control.Exception, which also has a function named catch; the import line simply hides the Prelude's catch in favour of Control.Exception's.

    However, that still leaves your more fundamental underlying question: how do you write conditionals in IO?

    Well, in this case, it would suffice to simply do

    when fileExists $ removeFile filename
    

    (using Control.Monad.when). But it's helpful here, as it usually is in Haskell, to look at the types.

    Both branches of a conditional must have the same type. So to fill in

    if fileExists
        then removeFile filename
        else ???
    

    we should look at the type of removeFile filename; whatever ??? is, it has to have the same type.

    System.Directory.removeFile has the type FilePath -> IO (), so removeFile filename has the type IO (). So what we want is an IO action with a result of type () that does nothing.

    Well, the purpose of return is to construct an action that has no effects, and just returns a constant value, and return () has the right type for this: IO () (or more generally, (Monad m) => m ()). So ??? is return () (which you can see I used in my improved snippet above, to do nothing when removeFile fails because the file doesn't exist).

    (By the way, you should now be able to implement when with the help of return (); it's really simple :))

    Don't worry if you find it hard to get into the Haskell way of things at first — it'll come naturally in time, and when it does, it's very rewarding. :)

    0 讨论(0)
  • 2021-02-05 03:24

    (Note: ehird's answer makes a very good point regarding a race condition. It should be kept in mind when reading my answer, which ignores the issue. Do note also that the imperative pseudo-code presented in the question also suffers from the same problem.)

    What defines the filename? Is it given in the program, or supplied by the user? In your imperative pseudo-code, it's a constant string in the program. I'll assume you want the user to supply it by passing it as the first command line argument to the program.

    Then I suggest something like this:

    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
    

    (As you can see, I added the type signature of doSomethingElse to avoid confusion).

    I import System.Environment for the getArgs function. In case the file in question is simply given by a constant string (such as in your imperative pseudo-code), just remove all the args stuff and fill in the constant string wherever I have head args.

    Control.Monad is imported to get the when function. Note that this useful function is not a keyword (like if), but an ordinary function. Let's look at its type:

    when :: Monad m => Bool -> m () -> m ()
    

    In your case m is IO, so you can think of when as a function that takes a Bool and an IO action and performs the action only if the Bool is True. Of course you could solve your problem with ifs, but in your case when reads a lot clearer. At least I think so.

    Addendum: If you, like I did at first, get the feeling that when is some magical and difficult machinery, it's very instructive to try to define the function yourself. I promise you, it's dead simple...

    0 讨论(0)
提交回复
热议问题