Getting Started

If you have a data type like:

import qualified Data.Vector         as V
import qualified Data.Vector.Mutable as MV

data MyType = MT
    { mtInt    :: Int
    , mtDouble :: Double
    , mtVec    :: V.Vector Double
    }
  deriving (Show, Generic)

Then you can give it an automatically derived mutable version:

instance Mutable s MyType where
    type Ref s MyType = GRef s MyType

Ref s MyType is now a "mutable MyType", like how MVector s a is a "mutable Vector a".

We now have some nice operations:

Whole-wise operations

Sometimes you just want to operate on the whole MyType. Well, you now have:

-- | Allocate a mutable 'MyType' in 'ST'
thawRef
    :: MyType
    -> ST s (Ref s MyType)

-- | "Freeze" a mutable 'MyType'
freezeRef
    :: Ref s MyType
    -> ST s MyType

-- | Overwrite a mutable 'MyType' with the contents of a pure one.
copyRef
    :: Ref s MyType
    -> MyType
    -> ST s ()

-- | Run an updating function on a whole 'MyType'
modifyRef
    :: Ref s MyType
    -> (MyType -> MyType)
    -> ST s ()

These actions are the types specialized ST, the mutable memory monad that comes with GHC. In truth, the types of these are more polymorphic and are generalized to work for all mutable monads with PrimMonad instance. The fully general types are:

-- | Allocate a mutable 'MyType' in the monad m
thawRef
    :: (PrimMonad m, PrimState m ~ s)
    => MyType
    -> m (Ref s MyType)

-- | "Freeze" a mutable 'MyType'
freezeRef
    :: (PrimMonad m, PrimState m ~ s)
    => Ref s MyType
    -> m MyType

-- | Overwrite a mutable 'MyType' with the contents of a pure one.
copyRef
    :: (PrimMonad m, PrimState m ~ s)
    => Ref s MyType
    -> MyType
    -> m ()

-- | Run an updating function on a whole 'MyType'
modifyRef
    :: (PrimMonad m, PrimState m ~ s)
    => Ref s MyType
    -> (MyType -> MyType)
    -> m ()

Piecewise Operations

This is nice, but we really the juicy stuff: a way to modify each part individually. For that, we have two main mechanisms: the field name based ones (using -XOverloadedLabels), and the position based ones (using -XTypeApplications). We have the continuation-based combinators:

-- | Do something with the 'Int' field
withField #mtInt
    :: (PrimMonad m, PrimState m ~ s)
    => Ref s MyType
    -> (MutVar s Int -> m r)
    -> m r

-- | Do something with the 'Vector' field
withField #mtVec
    :: (PrimMonad m, PrimState m ~ s)
    => Ref s MyType
    -> (MVector s Double -> m r)
    -> m r

-- | Do something with the second field, the Double
withPos @2
    :: (PrimMonad m, PrimState m ~ s)
    => Ref s MyType
    -> (MutVar s Double -> m r)
    -> m r

-- | Do something with a tuple of each ref in the type
withTuple
    :: (PrimMonad m, PrimState m ~ s)
    => Ref s MyType
    -> ((MutVar s Int, MutVar s Double, MVector s Double) -> m r)
    -> m r

And the MutPart-based ones, which yield a MutPart s b a (a way to "zoom into" a mutable a, if you have a mutable b), which can be used with functions like modifyPart and freezePart:

-- | Data type to "focus in" on the 'mtDouble' field in a 'MyType'
fieldMut #mtDouble
    :: MutPart s MyType Double

-- | Modify the 'Double' in the mutable 'MyType'
modifyPart (fieldMut #mtDouble)
    :: Ref s MyType
    -> (Double -> Double)
    -> m ()
-- | Data type to "focus in" on the first item in a 'MyType'
posMut @1
    :: MutPart s MyType Int

-- | Read out the 'Int' in the mutable 'MyType'
freezePart (posMut @1)
    :: Ref s MyPart
    -> s Int

Sum Types

We can get GRef for sum types too. As shown earlier, we get a mutable linked list type for free, and a nice "pop" function if we utilize constrMB:

data List a = Nil | Cons a (List a)
  deriving (Show, Generic)
infixr 5 `Cons`

instance Mutable s a => Mutable s (List a) where
    type Ref s (List a) = GRef s (List a)

consBranch
    :: Mutable s a
    => MutBranch s (List a) (a, List a)
consBranch = constrMB #_Cons

popStack
    :: (Mutable s a, PrimMonad m, PrimState m ~ s)
    => Ref s (List a)
    -> m (Maybe a)
popStack xs = do
    c <- projectBranch consBranch xs
    forM c $ \(y, ys) -> do
      o <- freezeRef y
      moveRef xs ys
      pure o

Read on for more information on how the library works, or jump right into the library with Haddock Documentation!