Подтвердить что ты не робот

Удалить файл, если он существует

Каков правильный способ сделать это в Haskell?

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

До сих пор я:

import System.Directory
main = do
        filename <- getFileNameSomehow
        fileExists <- doesFileExist filename
        if fileExists 
             then removeFile filename
             ???
        doSomethingElse
4b9b3361

Ответ 1

Вам лучше удалить файл и просто восстановить его, если он не существует:

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

Это позволяет избежать состояния гонки, когда кто-то удаляет файл между вашим кодом, проверяя, существует ли он и удаляет его. В вашем случае это может быть неважно, но в любом случае это хорошая практика.

Обратите внимание на строку import Prelude hiding (catch) - это потому, что Prelude содержит более старые функции из обработки исключений, которые теперь устарели в пользу Control.Exception, которая также имеет функцию с именем catch; строка импорта просто скрывает Prelude catch в пользу Control.Exception.

Однако это все еще оставляет ваш более фундаментальный основной вопрос: как вы записываете условные выражения в IO?

Ну, в этом случае достаточно просто сделать

when fileExists $ removeFile filename

(используя Control.Monad.when). Но полезно здесь, как это обычно бывает в Haskell, посмотреть на типы.

Обе ветки условного типа должны иметь один и тот же тип. Итак, чтобы заполнить

if fileExists
    then removeFile filename
    else ???

мы должны посмотреть на тип removeFile filename; независимо от ???, он должен иметь один и тот же тип.

System.Directory.removeFile имеет тип FilePath -> IO (), поэтому removeFile filename имеет тип IO (). Так что мы хотим, это действие IO с результатом типа (), который ничего не делает.

Ну, цель return состоит в том, чтобы построить действие, которое не имеет эффектов, и просто возвращает постоянное значение, а return () имеет для этого правильный тип: IO () (или, более общо, (Monad m) => m ()). Таким образом, ??? - это return () (который вы можете видеть, что я использовал в своем улучшенном фрагменте выше, ничего не делать, когда removeFile терпит неудачу, потому что файл не существует).

(Кстати, теперь вы можете реализовать when с помощью return (), это действительно просто:))

Не волнуйтесь, если вам сначала трудно проникнуть в хаскелские вещи - это со временем придет естественным образом, и когда это произойдет, это будет очень полезно.:)

Ответ 2

( Примечание: ehird answer дает очень хорошее представление о состоянии гонки. Его следует иметь в виду при чтении моего ответа, который игнорирует проблему. Также обратите внимание, что императивный псевдокод представленный в вопросе, также страдает от одной и той же проблемы.)

Что определяет имя файла? Дана ли она в программе или предоставляется пользователем? В вашем императивном псевдокоде это постоянная строка в программе. Я предполагаю, что вы хотите, чтобы пользователь предоставил его, передав его в качестве первого аргумента командной строки для программы.

Затем я предлагаю что-то вроде этого:

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

(Как вы можете видеть, я добавил сигнатуру типа doSomethingElse, чтобы избежать путаницы).

Я импортирую System.Environment для функции getArgs. В случае, если файл, о котором идет речь, просто задается константной строкой (например, в вашем императивном псевдокоде), просто удалите все элементы args и заполните константную строку везде, где у меня есть head args.

Control.Monad импортируется для получения функции when. Обратите внимание, что эта полезная функция не является ключевым словом (например, if), а обычной функцией. Давайте посмотрим на его тип:

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

В вашем случае m есть IO, поэтому вы можете думать о when как о функции, которая принимает Bool и IO-действие, и выполняет действие только в том случае, если Bool - True. Конечно, вы можете решить свою проблему с помощью if s, но в вашем случае when читается намного понятнее. По крайней мере, я так думаю.

Добавление: Если вы, как и я, сначала почувствуете, что when - это волшебная и сложная техника, очень поучительно пытаться определить функцию самостоятельно. Я обещаю вам, он мертв просто...