atomicModifyIORef 的额外结果参数的用途是什么?
What is the purpose of the extra result parameter of atomicModifyIORef?
modifyIORef
的签名很简单:
modifyIORef :: IORef a -> (a -> a) -> IO ()
不幸的是,这不是线程安全的。有一个替代方案可以解决这个问题:
atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b
这两个函数到底有什么区别?在修改可能从另一个线程读取的 IORef
时,我应该如何使用 b
参数?
额外参数用于提供 return 值。例如,您可能希望能够自动替换存储在 IORef
中的值和 return 中的旧值。你可以这样做:
atomicModifyIORef ref (\old -> (new, old))
如果您没有 return 的值,您可以使用以下方法:
atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()
atomicModifyIORef_ ref f =
atomicModifyIORef ref (\val -> (f val, ()))
与modifyIORef
具有相同的签名。
以下是我的理解。想想括号后面的函数,例如
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
这些函数将一个函数作为参数,return 该函数的 return 值。 atomicModifyIORef
与此类似。它以一个函数作为参数,目的是 return 该函数的 return 值。只有一个复杂的问题:参数函数还必须 return 一个新值存储在 IORef
中。因此,atomicModifyIORef
需要从该函数到 return 两个值。当然,这种情况与括号情况并不完全相似(例如不涉及IO
,我们不处理异常安全等),但这个类比给你一个想法。
正如您在评论中所述,如果没有并发性,您可以编写类似
的内容
modifyAndReturn ref f = do
old <- readIORef ref
let !(new, r) = f old
writeIORef r new
return r
但是在并发上下文中,其他人可以更改读取和写入之间的引用。
我喜欢通过 State
monad 来查看它。有状态操作修改一些内部状态并另外产生输出。这里的状态在 IORef
中,结果作为 IO
操作的一部分返回。所以我们可以使用 State
重新表述函数如下:
import Control.Monad.State
import Data.IORef
import Data.Tuple (swap)
-- | Applies a stateful operation to a reference and returns its result.
atomicModifyIORefState :: IORef s -> State s a -> IO a
atomicModifyIORefState ref state = atomicModifyIORef ref (swap . runState state)
modifyIORef
的签名很简单:
modifyIORef :: IORef a -> (a -> a) -> IO ()
不幸的是,这不是线程安全的。有一个替代方案可以解决这个问题:
atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b
这两个函数到底有什么区别?在修改可能从另一个线程读取的 IORef
时,我应该如何使用 b
参数?
额外参数用于提供 return 值。例如,您可能希望能够自动替换存储在 IORef
中的值和 return 中的旧值。你可以这样做:
atomicModifyIORef ref (\old -> (new, old))
如果您没有 return 的值,您可以使用以下方法:
atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()
atomicModifyIORef_ ref f =
atomicModifyIORef ref (\val -> (f val, ()))
与modifyIORef
具有相同的签名。
以下是我的理解。想想括号后面的函数,例如
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
这些函数将一个函数作为参数,return 该函数的 return 值。 atomicModifyIORef
与此类似。它以一个函数作为参数,目的是 return 该函数的 return 值。只有一个复杂的问题:参数函数还必须 return 一个新值存储在 IORef
中。因此,atomicModifyIORef
需要从该函数到 return 两个值。当然,这种情况与括号情况并不完全相似(例如不涉及IO
,我们不处理异常安全等),但这个类比给你一个想法。
正如您在评论中所述,如果没有并发性,您可以编写类似
的内容modifyAndReturn ref f = do
old <- readIORef ref
let !(new, r) = f old
writeIORef r new
return r
但是在并发上下文中,其他人可以更改读取和写入之间的引用。
我喜欢通过 State
monad 来查看它。有状态操作修改一些内部状态并另外产生输出。这里的状态在 IORef
中,结果作为 IO
操作的一部分返回。所以我们可以使用 State
重新表述函数如下:
import Control.Monad.State
import Data.IORef
import Data.Tuple (swap)
-- | Applies a stateful operation to a reference and returns its result.
atomicModifyIORefState :: IORef s -> State s a -> IO a
atomicModifyIORefState ref state = atomicModifyIORef ref (swap . runState state)