2014-02-27 62 views
2

我有這個很簡單的功能有沒有辦法從IO monad中打開一個類型?

import qualified Data.ByteString.Lazy as B 

getJson :: IO B.ByteString 
getJson = B.readFile jsonFile 

readJFile :: IO (Maybe Response) 
readJFile = parsing >>= (\d -> 
      case d of 
       Left err -> return Nothing 
       Right ps -> return (Just ps)) 
    where parsing = fmap eitherDecode getJson :: IO (Either String Response) 

其中jsonFile是對我的硬盤文件的路徑(原諒缺乏DO-符號的,但我發現這更清晰一起工作)

我問題是;有沒有辦法讓我去掉IO部分,這樣我就可以單獨處理字節串了?

我知道,你可以在某些單子像EitherMaybe得到他們的價值觀出模式匹配,但你可以做IO類似的東西?

或者有別於:有沒有辦法讓我readJFile返回Maybe Response沒有IO?

+0

可能的重複:http://stackoverflow.com/questions/8567743/how-to-extract-value-from-monadic-action – Teetoo

+0

我不是在談論內置的東西,但我只是要求做到這一點的一種方式,也許我可以構建自己;我的代碼看起來現在我必須通過5個函數拖動IO IO monad,並且公平地將它混亂很多,特別是考慮到我只在這個ONE函數中執行IO操作並且無法執行其他任何操作 –

+0

您從不需要通過任何函數「拖拽monad」,除非它們都需要實際執行IO。只需用'fmap'(或'liftM' /'liftM2'/...)將整個鏈條提升到monad中。 – leftaroundabout

回答

4

要擴大我的意見,這裏是你如何能做到這一點:

getJson :: IO B.ByteString 
getJson = B.readFile jsonFile -- as before 

readJFile :: B.ByteString -> Maybe Response -- look, no IO 
readJFile b = case eitherDecode b of 
       Left err -> Nothing 
       Right ps -> Just ps 

最後,你再一個IO動作相結合的一切:

getAndProcess :: IO (Maybe Response) 
getAndProcess = do 
    b <- getJson 
    return (readJFile b) 
+0

它很乾淨,很簡單,我喜歡!不幸的是它根本不起作用 –

+0

你的代碼給出了這個錯誤:http://pastebin.com/isb8SPn9 –

+0

@ElectricCoffee你發佈的錯誤似乎與我的代碼沒有多大關係。它提到了一行'Left err - > return Nothing',它沒有出現在我的代碼中,然後是另一行'Right ps - > return Just ps',它也沒有出現在我的代碼中。 – kosmikus

4

你永遠不需要通過任何函數「拖動monad」,除非它們都需要實際執行IO。只需將整個鏈條提升到fmap(或liftM/liftM2/...)。

例如,

f1 :: B.ByteString -> K 
f2 :: K -> I 
f3 :: K -> J 
f4 :: I -> J -> M 

和你的整個事情應該是像

m :: M 
m = let k = "f1 getJson" 
    in f4 (f2 k) (f3 k) 

的,你可以簡單地做

m = fmap (\b -> let k = f1 b 
       in f4 (f2 k) (f3 k)) 
    getJson 

順便說一句,這看起來可能與do更好表示法:

m = do 
    b <- getJson 
    return $ let k = f1 b 
      in f4 (f2 k) (f3 k) 

關於您編輯和問題

is there a way for me to make readJFile return Maybe Response without the IO ?

沒有,是不可能工作,因爲readJFile確實需要做的IO。沒有辦法從IO monad中逃脫,那就是它的全部要點! (嗯,有unsafePerformIO李嘉圖說,但這絕對不是一個有效的應用程序。)

如果它在IO單子拆包Maybe值的clunkiness,並與他們的括號簽名,您可能希望看看MaybeT transformer

readJFile' :: MaybeT IO Response 
readJFile' = do 
    b <- liftIO getJson 
    case eitherDecode b of 
    Left err -> mzero 
    Right ps -> return ps 
3

一般來說,是的,有一種方法。伴隨着很多「但」,但有。你在問什麼叫做不安全的IO操作System.IO.Unsafe。它通常用於在調用外部庫時編寫包裝器,這不是常規Haskell代碼中的訴求。

基本上,你可以打電話unsafePerformIO :: IO a -> a這正是你想要的,它剝去IO部分,並給你回包裝值a類型。但是,如果您查看文檔,您應該向系統保證自己的一系列要求,這些要求都以相同的想法結束:即使您通過IO執行操作,答案也應該是函數,正如其他任何不在IO中運行的haskell函數所預期的那樣:它應始終具有相同的結果,且無副作用,僅基於輸入值。

在這裏,鑑於您的代碼,這顯然不是,因爲您正在從文件中讀取數據。您應該繼續在IO monad中工作,通過在結果類型爲IO something的另一個函數中調用readJFile。然後,您將能夠讀取IO包裝中的值(您自己在IO中),對其進行處理,然後在返回時將結果重新包裝在另一個IO中。

+4

*永遠*不建議'unsafePerformIO'給初學者。即使只是提到,在某些情況下,它似乎是一個合理的選擇,但事實並非如此。初學者從不需要使用或知道'unsafePerformIO'。我提到這一點是因爲對於我和其他人來說,當我們瞭解Haskell時,我們知道'unsafePerformIO'實際上讓它難以學習,因爲它給人的印象是你應該在某個點上「爆發」IO monad ,你不是。 – kqr

+0

你真的讀了我的答案,先生容易downvote?我不推薦它,我只是陳述了一個事實。我的回答不是推動它,也不是錯誤的。如果你不喜歡它,不要高興。在某些情況下需要不安全的操作,當您必須與外部庫進行交互時(可能會爲它們編寫包裝)。好吧,這不是初學者的東西,但是,只要它是正確的,決定我應該什麼,不應該告訴人們什麼,並不取決於你。 –

+0

我也想指出,SO是爲了解答信息庫而存在的,這個信息可以讓任何人搜索信息。儘管他是初學者,並不需要unsafePerformIO,但將來可能會提供幫助,以供將來使用@kqr的人真正需要它。 –

2

沒有,沒有安全從IO monad中獲取值的方法。相反,您應該通過使用fmap或bind(>> =)應用函數來完成IO monad內部的工作。當你希望你的結果在Maybe中時,你也應該使用decode而不是orDecode。

getJson :: IO B.ByteString 
getJson = B.readFile jsonFile 

parseResponse :: B.ByteString -> Maybe Response 
parseResponse = decode 

readJFile :: IO (Maybe Response) 
readJFile = fmap parseResponse getJSON 

你也可以用做記號,如果這是更清楚你:

readJFile :: IO (Maybe Response) 
readJFile = do 
    bytestring <- getJson 
    return $ decode bytestring 

請注意,你不甚至不需要parseResponse功能,因爲readJFile指定類型。

相關問題