Mutable and Ref

Let's go over the high level view of what's going on. Conceptually, the entire library revolves around the Mutable typeclass and the Ref associated type.

class Mutable s a where
type Ref s a = v     | v -> a s

thawRef   :: (PrimMonad m, PrimState m ~ s) => a -> m (Ref s a)
freezeRef :: (PrimMonad m, PrimState m ~ s) => Ref s a -> m a
copyRef   :: (PrimMonad m, PrimState m ~ s) => Ref s a -> a -> m ()

-- ... plus some more methods that can be implemented using
-- the others in most cases


An instance of Mutable s a is an a that has a "mutable version" that can be updated/mutated in a "mutable PrimMonad m" (like IO or ST) that has a state token s.

The (injective) type family Ref associates every type a with its "mutable version".

(A quick note on PrimMonad --- it comes from the primitive library and is used across the ecosystem; it's a typeclass that abstracts over all "impure" monads like IO, ST s, ReaderT r IO, etc. You can think of it as an expanded version of MonadIO to also include monads that use ST s. PrimState is what you give to MutVar and MVector to make things "work properly")

For example, for Vector, the "mutable version" is an MVector:

class Mutable s (Vector a) where
type Ref s (Vector a) = MVector s a

thawRef   = V.thaw
freezeRef = V.freeze
copyRef   = V.copy


For simple non-composite data types like Int, you can just use a MutVar (a polymorphic version of IORef/STRef):

class Mutable s Int where
type Ref s Int = MutVar s Int

thawRef   = newMutVar
copyRef   = writeMutVar

class Mutable s Double where
type Ref s Int = MutVar s Double

thawRef   = newMutVar
copyRef   = writeMutVar


All we are doing so far is associating a type with its "mutable" version. But, what happens if we had some composite type?

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


We might imagine making a piecewise-mutable version of it, where each field is its own mutable reference:

data MyTypeRef s = MTR
{ mtrInt    :: MutVar s Int
, mtrDouble :: MutVar s Double
, mtrVec    :: MV.MVector s Double
}

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

thawRef (MT x y z) = MTR <$> newMutVar x <*> newMutVar y <*> V.thaw z freezeRef (MTR x y z) = MT <$> readMutVar x
<*> V.freeze   z

copyRef (MTR a b c) (MT x y z) = do
writeMutVar a x
writeMutVar b y
V.copy c z


But, this is pretty tedious to write for every single data type we have. What if we could instead automatically derive a reference type?

Well, we're in luck. If MyType is an instance of Generic, then we can just write:

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


We can now leave the rest of the typeclass body blank...and the mutable library will do the rest for us!

• GRef s MyType is an automatically derived type that is equivalent to the MyTypeRef that we wrote earlier. It leverages the power of GHC generics and typeclasses. Every field of type X turns into a field of type Ref s X. This "does the right thing" as long as all your fields are instances of Mutable.
• The mechanisms in DefaultMutable will automatically fill in the rest of the typeclass for you.