3

我有这个非常简单的功能

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-notation,但我发现使用起来更清楚)

我的问题是;有没有办法让我放弃这个IO部分,这样我就可以单独使用字节串了?

我知道您可以对某些 monad 进行模式匹配,例如EitherMaybe获取它们的值,但是您可以用 做类似的事情IO吗?

或者换一种说法:有没有办法让我在没有 IO 的情况下readJFile返回?Maybe Response

4

4 回答 4

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)

关于您的编辑和问题

有没有办法让我在没有的情况下readJFile返回?Maybe ResponseIO

,这不可能工作,因为readJFile确实需要做 IO。那么就没有办法从IOmonad 中逃脱,这就是它的全部意义所在!(嗯,unsafePerformIO正如里卡多所说,但这绝对不是一个有效的应用程序。)

如果是在monad 中解包Maybe值的笨拙IO,以及其中带有括号的签名,您可能需要查看MaybeTtransformer

readJFile' :: MaybeT IO Response
readJFile' = do
   b <- liftIO getJson
   case eitherDecode b of
     Left err -> mzero
     Right ps -> return ps
于 2014-02-27T09:01:31.797 回答
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)
于 2014-02-27T09:06:41.270 回答
2

一般来说,是的,有一种方法。伴随着很多“但是”,但是有。您正在询问所谓的不安全 IO 操作System.IO.Unsafe。它通常用于在调用外部库时编写包装器,这不是在常规 Haskell 代码中使用的东西。

基本上,您可以调用unsafePerformIO :: IO a -> awhich 完全符合您的要求,它会剥离IO部分并返回 type 的包装值a。但是,如果您查看文档,您应该向系统保证一些要求,这些要求都以相同的想法结束:即使您通过 IO 执行操作,答案也应该是函数,正如任何其他不在其中运行的 haskell 函数所期望的那样IO:它应该始终具有相同的结果而没有副作用,仅基于输入值。

在这里,给定您的代码,这显然不是这种情况,因为您正在从文件中读取。您应该继续在 IO monad 中工作,方法是readJFile从另一个函数中调用您的结果类型IO something。然后,您将能够读取IO包装器中的值(在您IO自己中),对其进行处理,然后在返回时将结果重新包装到另一个中IO

于 2014-02-27T09:02:58.380 回答
2

不,没有安全的方法可以从 IO monad 中获取值。相反,您应该通过使用 fmap 或 bind (>>=) 应用函数来完成 IO monad 内部的工作。此外,当您希望结果为 Maybe 时,您应该使用 decode 而不是 eitherDecode。

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

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

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

如果您更清楚,您也可以使用 do 表示法:

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

请注意,您甚至不需要 parseResponse 函数,因为 readJFile 指定了类型。

于 2014-02-27T09:29:28.860 回答