Haskel:如何强制评估函数并按顺序写入文件?
Haskel: how to force evaluation of functions and write to a file sequentially?
我在 Haskell 中遇到惰性 IO 问题。尽管阅读了该领域的其他问题,但我无法弄清楚如何解决我的具体案例。
我正在使用 scalpel 包来解析 html。用例很简单:一个站点包含指向描述某种事件的其他站点的链接。所以我写了以下结构(我在这里省略了一些实现):
type Url = String
-- function that parses all urls
allUrls :: Url -> IO (Maybe [Url])
data Event = Event { ... }
-- function that parses an event
parseEvent :: Url -> IO (Maybe Event)
-- function that writes the event to a file
doThings :: Url -> IO ()
doThings url = return url >>= parseEvent >>= (appendFile "/tmp/foo.txt" . show)
-- function that should take all urls and write their events to a file
allEvents :: IO (Maybe [Url]) -> IO (Maybe (IO [()]))
allEvents urls = urls >>= return . liftM (mapM doThings)
-- or alternatively:
-- function that takes all urls and returns all events
allEvents :: IO (Maybe [Url]) -> IO (Maybe (IO [Maybe Event]))
allEvents urls = urls >>= return . liftM (mapM parseEvent)
-- some function that writes all events to a file
allEventsToFile :: IO (Maybe (IO [Maybe Event])) -> IO()
???
doThings 函数按预期工作。给定一个 url,它解析对应的事件并将其写入文件。但是由于懒惰,allEvents 什么都不做。如何在 allEvents 中强制执行评估?
这是不是懒惰IO的问题。惰性 IO 是指您从文件中读取惰性字符串,但不对其求值——在这种情况下,运行时将延迟实际读取 直到 您对其求值。
问题实际上是你 在 allEvents
中没有做任何 IO – 你只是在 IO
函子。这些值恰好是 IO
操作本身,但这并不重要。具体来说,根据 monad 法则,a >>= return . f
始终与 fmap f a
相同。 IO中的fmapping不绑定动作。
此问题已在类型签名中观察到:-> IO (Maybe (IO [()]))
表示该函数会生成稍后可以执行的 IO 操作。但是在这种情况下,你想在执行allEvents
时执行所有内容。所以签名可以是
allEvents :: IO (Maybe [Url]) -> IO ()
(或者 -> IO (Either EventExecError ())
,如果你想正确处理失败)。
这可能仍然不是您想要的:为什么您 将 一个 IO
动作作为参数?这意味着 allEvents
本身需要执行该操作以首先获取 URL,然后再执行它自己的任何工作。这可能有其自身的副作用,并且会针对不同的调用给出不同的结果,您想要吗?
我猜不是,所以真的应该是
allEvents :: Maybe [Url] -> IO ()
现在您从一个简单的 Maybe
值开始,您可以轻松地对其进行模式匹配:
allEvents Nothing = ? -- perhaps simply `return ()`
allEvents (Just urls) = mapM_ doThings urls
然后要在您的程序中使用它,您需要将 url-获取绑定到事件执行:
main :: IO ()
main = do
urlq <- allUrls
allEvents urlq
...或简称 allUrls >>= allEvents
.
我在 Haskell 中遇到惰性 IO 问题。尽管阅读了该领域的其他问题,但我无法弄清楚如何解决我的具体案例。
我正在使用 scalpel 包来解析 html。用例很简单:一个站点包含指向描述某种事件的其他站点的链接。所以我写了以下结构(我在这里省略了一些实现):
type Url = String
-- function that parses all urls
allUrls :: Url -> IO (Maybe [Url])
data Event = Event { ... }
-- function that parses an event
parseEvent :: Url -> IO (Maybe Event)
-- function that writes the event to a file
doThings :: Url -> IO ()
doThings url = return url >>= parseEvent >>= (appendFile "/tmp/foo.txt" . show)
-- function that should take all urls and write their events to a file
allEvents :: IO (Maybe [Url]) -> IO (Maybe (IO [()]))
allEvents urls = urls >>= return . liftM (mapM doThings)
-- or alternatively:
-- function that takes all urls and returns all events
allEvents :: IO (Maybe [Url]) -> IO (Maybe (IO [Maybe Event]))
allEvents urls = urls >>= return . liftM (mapM parseEvent)
-- some function that writes all events to a file
allEventsToFile :: IO (Maybe (IO [Maybe Event])) -> IO()
???
doThings 函数按预期工作。给定一个 url,它解析对应的事件并将其写入文件。但是由于懒惰,allEvents 什么都不做。如何在 allEvents 中强制执行评估?
这是不是懒惰IO的问题。惰性 IO 是指您从文件中读取惰性字符串,但不对其求值——在这种情况下,运行时将延迟实际读取 直到 您对其求值。
问题实际上是你 在 allEvents
中没有做任何 IO – 你只是在 IO
函子。这些值恰好是 IO
操作本身,但这并不重要。具体来说,根据 monad 法则,a >>= return . f
始终与 fmap f a
相同。 IO中的fmapping不绑定动作。
此问题已在类型签名中观察到:-> IO (Maybe (IO [()]))
表示该函数会生成稍后可以执行的 IO 操作。但是在这种情况下,你想在执行allEvents
时执行所有内容。所以签名可以是
allEvents :: IO (Maybe [Url]) -> IO ()
(或者 -> IO (Either EventExecError ())
,如果你想正确处理失败)。
这可能仍然不是您想要的:为什么您 将 一个 IO
动作作为参数?这意味着 allEvents
本身需要执行该操作以首先获取 URL,然后再执行它自己的任何工作。这可能有其自身的副作用,并且会针对不同的调用给出不同的结果,您想要吗?
我猜不是,所以真的应该是
allEvents :: Maybe [Url] -> IO ()
现在您从一个简单的 Maybe
值开始,您可以轻松地对其进行模式匹配:
allEvents Nothing = ? -- perhaps simply `return ()`
allEvents (Just urls) = mapM_ doThings urls
然后要在您的程序中使用它,您需要将 url-获取绑定到事件执行:
main :: IO ()
main = do
urlq <- allUrls
allEvents urlq
...或简称 allUrls >>= allEvents
.