当将括号与PTR作为资源使用时,可以将其替换为外国PTR吗?
我的代码使用可以描述为指针的资源。为简单起见,我将在此处使用void
指针。计算完成后必须关闭资源,因此control.exception.bracket
函数是自然的选择,以确保如果发生错误,则代码不会泄漏:
run :: (Ptr () -> IO a) -> IO a
run action = bracket acquireResource closeResource action
-- no eta reduction for clarity
此模式的缺点是在Action
完成后,资源将始终关闭。 Afaiu这意味着不可能执行诸如
cont <- run $ \ptr -> do
a <- someAction ptr
return (\x -> otherActionUsingResource ptr a x)
cont ()
资源之类的事情已经在执行时间cont
之前。现在,我的方法是使用外ptr:
run' :: (ForeignPtr () -> IO a) -> IO a
run' action = do
ptr <- acquireResource
foreignPtr <- newForeignPtr closeResourceFunPtr ptr
action foreignPtr
现在似乎这大致相当于第一个版本,较小的打字差异和资源结束了延迟。但是,我确实想知道这是真的,还是我错过了什么。某些错误条件可以导致这两个版本的不同结果吗?外国PTR可以安全使用吗?
My code uses a resource that can be described as a pointer; I'll use a void
pointer here for simplicity. The resource must be closed after the computation with it finishes, so the Control.Exception.bracket
function is a natural choice to make sure the code won't leak if an error occurs:
run :: (Ptr () -> IO a) -> IO a
run action = bracket acquireResource closeResource action
-- no eta reduction for clarity
The downside of this pattern is that the resource will always be closed after action
completes. AFAIU this means that it isn't possible to do something like
cont <- run $ \ptr -> do
a <- someAction ptr
return (\x -> otherActionUsingResource ptr a x)
cont ()
The resource will already be close by the time cont
is executed. Now my approach is to use a ForeignPtr instead:
run' :: (ForeignPtr () -> IO a) -> IO a
run' action = do
ptr <- acquireResource
foreignPtr <- newForeignPtr closeResourceFunPtr ptr
action foreignPtr
Now it seems that this is roughly equivalent to the first version, minor typing differences and resource closing latency aside. However, I do wonder whether this is true, or if I miss something. Can some error conditions can lead to different outcomes with those two versions? Are ForeignPtr safe to use in this way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您想这样做,我建议您避免使用
运行'
,这使您看起来好像要关闭资源。做这样的事情。正如卡尔(Carl)在评论中指出的那样,重要的是,在获取资源和安装最终化器以关闭它之间掩盖了例外。否则,可能会在之间提供异步异常,并导致资源泄漏。
这种挑战在于,您将其保留给用户和/或垃圾收集器,以确保资源释放。原始的
ptr
基于基于的代码使寿命显式。现在不是。许多人认为,明确的寿命更适合关键资源。foreferptr
为您提供了GC自动完成,这些人认为设计差。所以仔细考虑!这是您最终想释放的便宜资源(例如malloc
ed内存)吗?还是您真的想确定的是昂贵的东西(例如文件描述符)?旁注:
ptr()
和foreferptr()
不是很惯用。通常,类型的参数应该是代表所指向的任何内容的haskell类型。If you want to do this, I'd recommend avoiding that
run'
, which makes it look like you're going to close the resource. Do something like this instead.As Carl pointed out in a comment, it's important that exceptions be masked between acquiring the resource and installing the finalizer to close it; otherwise an asynchronous exception could be delivered in between and cause a resource leak.
The challenge with anything of this sort is that you're leaving it up to the user and/or garbage collector to make sure the resource gets freed. The original
Ptr
-based code made the lifespan explicit. Now it's not. Many people believe that explicit lifespans are better for critical resources. WhatForeignPtr
gives you, automatic finalization by the GC, these people consider poor design. So think carefully! Is this a cheap resource (like a little bit ofmalloc
ed memory) that you just want to free eventually? Or is it something expensive (like a file descriptor) that you really want to be sure about?Side note:
Ptr ()
andForeignPtr ()
aren't very idiomatic. Usually the type argument should be a Haskell type representing whatever is being pointed to.