2016-01-08 138 views
1

我試圖寫一個簡單的函數來安全地讀取一個文件(如果存在),什麼也不做,如果該文件不存在:哈斯克爾安全IO

safeRead :: String -> IO() 
safeRead path = readFile path `catch` handleExists 
    where handleExists e 
      | isDoesNotExistError e = return() 
      | otherwise = throwIO e 

這種失敗在編譯時有

Couldn't match type ‘[Char]’ with ‘()’ 
Expected type: IO() 
    Actual type: IO String 
In the first argument of ‘catch’, namely ‘readFile path’ 
In the expression: readFile path `catch` handleExists 

這是有道理的,因爲:t readFilereadFile :: FilePath -> IO String。例如,返回IO String(和IO String是不一樣的IO()

更改簽名String -> IO String

Couldn't match type ‘()’ with ‘[Char]’ 
Expected type: IOError -> IO String 
    Actual type: IOError -> IO() 
In the second argument of ‘catch’, namely ‘handleExists’ 
In the expression: readFile path `catch` handleExists 

這也使得自handleExists的類型爲IO()

爲了節省大家的查找意義上的功能,漁具是用進口的: import Control.Exception 漁獲簽名是: catch :: Exception e => IO a -> (e -> IO a) -> IO a

我真正的問題是,我如何在Haskell中編寫這種錯誤安全,靈活的代碼?更具體地說,我將不得不對這個函數做出什麼改變,使它能夠處理成功案例和失敗案例?

+0

您的代碼爲我編譯使'isDoesNotexistError'總是返回true,所以問題可能在其他部分? – Netwave

+0

是的,剛纔檢查,throwIO是返回一個'IO了','不IO()' – Netwave

+4

對我來說,似乎你正在尋找的簽名是'字符串 - > IO(也許字符串)' – Carsten

回答

5

你需要弄清楚你想讓你的函數實際做什麼。

如果它成功讀取文件,您希望它以字符串形式返回內容。

如果失敗了,你究竟希望它做什麼?返回一個空字符串或其他一些後備內容?然後,您可以在handleExists的第一個案例中將return()更改爲return ""

但是如果你想以指示返回類型的錯誤,那麼你需要的不僅僅是String返回不同的類型。正如Carsten所說的,你可以返回Maybe String,並給出Just theString成功,Nothing出錯。或者,如果您想要一些錯誤消息,則可以返回Either

我覺得,爲了這個特別的功能,Maybe String是很有道理的,因爲你只抓住文件不存在,並再次引發其他錯誤。那麼你的代碼需要看起來像這樣:

safeRead :: String -> IO (Maybe String) 
safeRead path = (fmap Just $ readFile path) `catch` handleExists 
    where 
    handleExists :: IOException -> IO (Maybe String) 
    handleExists e 
     | isDoesNotExistError e = return Nothing 
     | otherwise = throwIO e 

這裏我們總結的readFile結果在Just履行型需求,並在錯誤的情況下返回Nothing而不是單位。

+0

我不認爲它會以這種方式工作 - 國際海事組織必須通過給這樣的'Exception'實例來幫助編譯器:'handleExists :: IOException - > IO(Maybe String)...'...是的 - 當我嘗試你的時候,Haskell抱怨着;) - 我允許自己將你的工作狀態改變爲工作狀態 - 如果你不喜歡它,就回滾一下 – Carsten

+0

當然如果'isDooesNotExistError'有權限的話你可能不需要這個簽名...我很懶,只是'isDoesNotExistError = undefined';) – Carsten

+0

@Carsten我不知道,我沒有真正編譯它。 'isDoesNotExistError'具有正確的簽名,所以我會假定它沒有簽名就編譯,但它也沒有傷害。 –