我有这个非常简单的功能

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?

有帮助吗?

解决方案

在我的评论中扩展,这是你可以做到的:

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
.

最后,您将再次将所有内容组合在一起:

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

其他提示

您永远不需要通过任何函数“拖动 monad”,除非它们都需要实际执行 IO。只需将整个链提升到 monad 中即可 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 Response 没有 IO?

, ,这不可能行得通,因为 readJFile 确实需要做IO。没有办法逃脱 IO monad 那么,这就是它的重点!(嗯,有 unsafePerformIO 正如里卡多所说,但这绝对不是一个有效的应用程序。)

如果是因为拆包的笨拙 Maybe 中的值 IO monad 以及其中带有括号的签名,您可能需要查看 MaybeT 变压器.

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

总的来说,是的,有办法。伴随着很多“但是”,但是有。你问的是所谓的 不安全的IO操作: 系统.IO.不安全. 。它通常用于在调用外部库时编写包装器,在常规 Haskell 代码中不应该使用它。

基本上你可以打电话 unsafePerformIO :: IO a -> a 这正是你想要的,它去掉了 IO 部分并返回类型的包装值 a. 。但是,如果您查看文档,您应该向系统保证一些要求,这些要求最终都具有相同的想法:即使您通过 IO 执行操作,答案也应该是函数的结果,正如任何其他不在其中运行的 haskell 函数所预期的那样 IO:仅基于输入值,它应该始终具有相同的结果,没有副作用。

在这里,给出您的代码, 显然情况并非如此, ,因为您正在读取文件。你应该继续在 IO monad 中工作,通过调用你的 readJFile 来自另一个具有结果类型的函数 IO something. 。然后,您将能够读取其中的值 IO 包装器(在 IO 你自己),对其进行处理,然后将结果重新包装在另一个中 IO 回来时。

No, there is no safe way to get a value out of the IO monad. Instead you should do the work inside the IO monad by applying functions with fmap or bind (>>=). Also you should use decode instead of eitherDecode when you want your result to be in Maybe.

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

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

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

You could also use do notation if that is clearer to you:

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

Note that you dont even need the parseResponse function since readJFile specifies the type.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top