我有这个非常简单的功能
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
是我硬盘上文件的路径(原谅缺少写法,但我发现这更清楚了)
我的问题是;有没有办法抛弃IO
部分,这样我就可以单独处理字节串了?
我知道你可以在Either
和Maybe
之类的某些monad上进行模式匹配以获取其值,但是你可以使用IO
做类似的事情吗?
或者表达方式不同:有没有办法让readJFile
在没有IO的情况下返回Maybe Response
?
答案 0 :(得分:4)
你永远不需要通过任何函数“拖动monad”,除非他们都需要实际执行IO。只需使用fmap
(或liftM
/ liftM2
/ ...)将整个链条提升到monad中。
例如,
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
monad中解包IO
值以及包含parens的签名是笨拙的话,您可能需要查看MaybeT
transformer。
readJFile' :: MaybeT IO Response
readJFile' = do
b <- liftIO getJson
case eitherDecode b of
Left err -> mzero
Right ps -> return ps
答案 1 :(得分: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)
答案 2 :(得分:3)
总的来说,是的,有办法。伴随着很多“但是”,但也有。您要求的是什么叫做不安全的IO操作:System.IO.Unsafe。它通常用于在调用外部库时编写包装器,而不是在常规Haskell代码中使用它。
基本上,您可以调用unsafePerformIO :: IO a -> a
来完成您想要的操作,它会删除IO
部分,并为您提供类型为a
的包装值。但是,如果你看一下文档,你应该向系统保证一些要求,这些要求都是相同的想法:即使你通过IO执行操作,答案也应该是函数,正如任何其他haskell函数所期望的那样,它不在IO
中运行:它应该始终具有相同的结果而没有副作用,仅基于输入值。
在这里,鉴于您的代码,显然不是这种情况,因为您正在从文件中读取。您应该继续在IO monad中工作,方法是在结果类型为readJFile
的另一个函数中调用IO something
。然后,您将能够读取IO
包装器中的值(自己在IO
中),对其进行处理,然后在返回时将结果重新包装到另一个IO
中
答案 3 :(得分:2)
不,没有安全的方法可以从IO monad中获取值。相反,你应该通过应用fmap或bind(&gt;&gt; =)函数来完成IO monad中的工作。当你希望你的结果在Maybe中时,你应该使用decode而不是任何Decode。
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode
readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON
如果您更清楚,也可以使用do notation:
readJFile :: IO (Maybe Response)
readJFile = do
bytestring <- getJson
return $ decode bytestring
请注意,您甚至不需要parseResponse函数,因为readJFile指定了类型。