【问题标题】:Haskel: how to force evaluation of functions and write to a file sequentially?Haskel:如何强制评估函数并按顺序写入文件?
【发布时间】:2019-04-19 08:50:03
【问题描述】:

我在 Haskell 中遇到了惰性 IO 问题。尽管阅读了该领域的其他问题,但我无法弄清楚如何解决我的具体案例。

我正在使用手术刀包来解析 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 中进行评估?

【问题讨论】:

  • 你需要你的allEventsIO (Maybe [Url]) -> IO ()的类型

标签: haskell file-io io html-parsing lazy-evaluation


【解决方案1】:

不是惰性IO的问题。惰性 IO 是当您从文件中读取惰性字符串但不对其进行评估时——在这种情况下,运行时将推迟实际读取直到您评估它。

问题实际上是您没有在allEvents 中执行任何IO——您只是在IO 仿函数 中推挤值。这些值恰好是IO 操作本身,但这并不重要。具体来说,根据单子定律,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-fetching 绑定到事件执行:

main :: IO ()
main = do
  urlq <- allUrls
  allEvents urlq

...或简称allUrls &gt;&gt;= allEvents

【讨论】:

  • 谢谢,这行得通,实际上也更直观,更简单。我不知道为什么我没有首先想到这个。我认为只需将allUrls 的结果刷新到allEvents 中就可以了,而不是一元地组合函数。而且我认为建立一个 IO 操作列表然后强制对其进行评估是可以的。
  • 这是在 Haskell 中熟悉的一个非常基本的事情:评估一个 IO 动作并执行它;仅将其绑定到 main 即可。
猜你喜欢
  • 1970-01-01
  • 2018-11-05
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
  • 2012-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多