1module RIO.Prelude.URef
2  ( URef
3  , IOURef
4  , newURef
5  , readURef
6  , writeURef
7  , modifyURef
8  ) where
9
10import RIO.Prelude.Reexports
11import qualified Data.Vector.Unboxed.Mutable as MUVector
12
13-- | An unboxed reference. This works like an 'IORef', but the data is
14-- stored in a bytearray instead of a heap object, avoiding
15-- significant allocation overhead in some cases. For a concrete
16-- example, see this Stack Overflow question:
17-- <https://stackoverflow.com/questions/27261813/why-is-my-little-stref-int-require-allocating-gigabytes>.
18--
19-- The first parameter is the state token type, the same as would be
20-- used for the 'ST' monad. If you're using an 'IO'-based monad, you
21-- can use the convenience 'IOURef' type synonym instead.
22--
23-- @since 0.0.2.0
24newtype URef s a = URef (MUVector.MVector s a)
25
26-- | Helpful type synonym for using a 'URef' from an 'IO'-based stack.
27--
28-- @since 0.0.2.0
29type IOURef = URef (PrimState IO)
30
31-- | Create a new 'URef'
32--
33-- @since 0.0.2.0
34newURef :: (PrimMonad m, Unbox a) => a -> m (URef (PrimState m) a)
35newURef a = fmap URef (MUVector.replicate 1 a)
36
37-- | Read the value in a 'URef'
38--
39-- @since 0.0.2.0
40readURef :: (PrimMonad m, Unbox a) => URef (PrimState m) a -> m a
41readURef (URef v) = MUVector.unsafeRead v 0
42
43-- | Write a value into a 'URef'. Note that this action is strict, and
44-- will force evalution of the value.
45--
46-- @since 0.0.2.0
47writeURef :: (PrimMonad m, Unbox a) => URef (PrimState m) a -> a -> m ()
48writeURef (URef v) = MUVector.unsafeWrite v 0
49
50-- | Modify a value in a 'URef'. Note that this action is strict, and
51-- will force evaluation of the result value.
52--
53-- @since 0.0.2.0
54modifyURef :: (PrimMonad m, Unbox a) => URef (PrimState m) a -> (a -> a) -> m ()
55modifyURef u f = readURef u >>= writeURef u . f
56