如何在Haskell中实现forking try catch?

如何在Haskell中实现forking try catch?,haskell,try-catch,fork,Haskell,Try Catch,Fork,我想写一个函数 forkos_try :: IO (Maybe α) -> IO (Maybe α) 它接受一个命令xx是一个命令式操作,它首先改变状态,然后检查该状态是否混乱。(它不做任何外部操作,这需要某种操作系统级别的沙箱来恢复状态。) 如果x的计算结果为Just y,forkos\u try返回Just y 否则,forkos\u try回滚状态,并返回Nothing 在内部,它应该fork()进入线程parent和child,并在child上运行x 如果x成功,child

我想写一个函数

forkos_try :: IO (Maybe α) -> IO (Maybe α)
它接受一个命令
x
x
是一个命令式操作,它首先改变状态,然后检查该状态是否混乱。(它不做任何外部操作,这需要某种操作系统级别的沙箱来恢复状态。)

  • 如果
    x
    的计算结果为
    Just y
    forkos\u try
    返回
    Just y
  • 否则,
    forkos\u try
    回滚状态,并返回
    Nothing
在内部,它应该
fork()
进入线程
parent
child
,并在
child
上运行
x

  • 如果
    x
    成功,
    child
    应继续运行(返回
    x
    的结果),而
    parent
    应死亡
  • 否则,
    父级
    应继续运行(返回
    ),而
    子级
    应死亡
问:用什么方法来编写与forkos\u try等价或比之更强大的语义N.B.--变异的状态(通过
x
位于外部库中,不能在线程之间传递。因此,哪个线程保持活动状态的语义非常重要

形式上,“keep running”的意思是“执行一些continuation
rest::Maybeα->IO()
”。但是,该延续在代码中没有任何明确的地方

就我的情况而言,我认为(目前)使用
forkOS
(需要整个计算
child
才能运行)以不同的风格编写它是可行的,因为我可以为
rest
编写一个显式表达式。但是,困扰我的是,我不知道如何使用原语函数
forkOS
——人们会认为它足够通用,可以支持任何特定的情况(它可以作为高级API出现,比如
forkOS\u try

编辑——如果问题仍然不清楚,请查看带有显式
rest
的示例代码[]


p、 我已经有一段时间没有写并发代码了;希望我对POSIX
fork()
的了解是正确的!提前感谢。

如果您显式地对状态建模,那么事情就简单得多了

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等等),所以我在这里看到了两种选择之一:

  • 克隆世界,并在沙箱中运行操作。如果它成功了,那么继续在现实世界中运行该操作
  • 克隆世界,并在真实世界中运行操作。如果失败,则使用先前拍摄的快照替换现实世界
  • 当然,这两种方法实际上是相同的:用叉子叉世界。一个世界主导行动,而另一个世界则不然。如果行动成功,那么这个世界将继续下去;否则,另一个世界将继续。您建议通过构建
    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)
    
    tryIO::ios->(s->IO())->IO(可能是a)->IO(可能是a)
    tryIO save restore action=do
    initialState>不返回任何内容
    Just x->return(Just x)
    

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

    这里是一个新手实现,它有一个明确的延续:我很难理解您的问题。状态似乎在IO单子中,但您希望回滚-因为这通常无法完成(例如,可能您发射了一些导弹),如何回滚状态?我认为OP希望使用操作系统级别的fork(即,不是
    forkIO
    )。这将允许您回滚所有内部状态,如
    IORef
    s。当然,它不能回滚写入文件的内容。@gatoatigrado如果使用forkOS,您的解决方案将非常繁重(例如,子进程必须启动新的IO管理器和其他运行时服务)。既然您只想改变状态,您是否考虑过使用
    ST
    monad?这允许您拥有状态,但不要求您处于IO中。注意:
    forkOS
    不是
    fork()
    。也许你的意思是?对不起,我用粗体字说,
    s
    不能在线程之间传递;特别是,它也不能转换为Haskell类型。这是C库中存在的一些状态。注意:
    forkOS
    只是启动一个线程,就像
    forkIO
    ,但绑定到一个新的操作系统级线程。要分叉这个过程,您需要@gatoatigrado我建议的解决方案是单线程的,所以这不是问题。但是你没有办法拍下这个状态?如果您希望能够回滚,那么这将是一个相当大的问题。除了求助于进程分叉之外,没有其他手段是一种非常强烈的代码气味。@DanBurton,是的,求助于进程分叉是不明智的