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.