2014-02-27 63 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指定类型。

相关问题