如何在Haskell中实现forking try-catch?

时间:2012-02-29 10:14:02

标签: haskell try-catch fork

我想写一个函数

forkos_try :: IO (Maybe α) -> IO (Maybe α)

采用命令xx是一个命令式操作,它首先改变状态,然后检查该状态是否混乱。 (它不执行任何外部操作,这需要某种操作系统级别的沙盒来恢复状态。)

  • 如果x评估为Just y,则forkos_try会返回Just y
  • 否则,forkos_try 回滚状态,并返回Nothing

在内部,它应该fork()进入线程parentchildx上运行child

  • 如果x成功,child应继续投放(返回x的结果),parent应该会死
  • 否则,parent应继续投放(返回Nothing),child应该

问题:使用与forkos_try相同或更强大的语义编写内容的方法是什么?的 N.B。 - 状态变异(由x位于外部库中,无法在线程之间传递。因此,保持活动的线程的语义很重要。

正式地,“继续运行”意味着“执行一些延续rest :: Maybe α -> IO ()”。但是,这种延续并没有在代码中保持明确。

对于我的情况,我认为(当时)会以不同的方式编写它,使用forkOS(它将运行整个计算child),因为我可以写一个rest的显式表达式。但是,我无法弄清楚如何使用原始函数forkOS让我感到麻烦 - 人们会认为它足以支持任何特定情况(可能看起来像是一个高级API,比如forkos_try)。

编辑 - 如果问题仍然不明确[http://pastebin.com/nJ1NNdda],请查看带有明确rest的示例代码。

P.S。我暂时没有编写并发代码;希望我对POSIX fork()的了解是正确的!提前谢谢。

1 个答案:

答案 0 :(得分:2)

如果你明确地建模状态,那么事情要简单得多。

someStateFunc :: (s -> Maybe (a, s))

-- inside some other function
case someStateFunc initialState of
  Nothing -> ... -- it failed. stick with initial state
  Just (a, newState) -> ... -- it suceeded. do something with
                            -- the result and new state

对于不可变状态,“回滚”很简单:继续使用initialState。而“不回滚”也很简单:只需使用newState

所以......我从你的解释中假设这个“外部库”执行一些非常重要的IO效果,但这些效果仅限于一些可知和可逆的操作(修改文件,IORef等)。没有办法扭转一些事情(发射导弹,写入stdout等),所以我在这里看到两个选择之一:

  1. 克隆世界,并在沙箱中运行操作。如果成功,那就继续在真实世界中运行。
  2. 克隆世界,并在现实世界中运行动作。如果失败,则用你之前拍摄的快照替换真实世界。
  3. 当然,这两者实际上都是相同的方法:分叉世界。一个世界在运行,一个世界没有。如果行动成功,那么这个世界将继续存在;否则,另一个世界继续。您建议通过构建forkOS来实现此目的,这将克隆程序的整个状态,但这不足以处理,例如,文件修改。请允许我建议一种更接近于不可变状态的简单性的方法:

    tryIO :: IO s -> (s -> IO ()) -> IO (Maybe a) -> IO (Maybe a)
    tryIO save restore action = do
      initialState <- save
      result <- action
      case result of
        Nothing -> restore initialState >> return Nothing
        Just x  -> return (Just x)
    

    在这里,您必须提供一些数据结构s,以及从所述数据结构到saverestore的方法。这使您可以灵活地执行任何您认为必要的克隆。 (例如save可以将某个文件复制到临时位置,然后restore可以将其复制回来并删除临时文件。或者save可以复制某些IORef的值,然后restore可以将价值放回去。)这种方法可能不是最有效的方法,但它非常简单。