mutable-0.2.0.0: src/Data/Mutable/Class.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Data.Mutable.Class
-- Copyright : (c) Justin Le 2020
-- License : BSD3
--
-- Maintainer : justin@jle.im
-- Stability : experimental
-- Portability : non-portable
--
-- Provides the 'Mutable' typeclass and various helpers. See
-- "Data.Mutable" for the main "entrypoint". Many of the datatypes used
-- for 'Ref' instances are defined in "Data.Mutable.Instances"
module Data.Mutable.Class (
Mutable(..)
, copyRefWhole, moveRefWhole, cloneRefWhole
, modifyRef, modifyRef'
, updateRef, updateRef'
, modifyRefM, modifyRefM'
, updateRefM, updateRefM'
, RefFor(..)
, DefaultMutable(..)
-- * Providing and overwriting instances
, VarMut(..)
, CoerceMut(..)
, TraverseMut(..)
, Immutable(..)
-- * Util
, MapRef
) where
import Control.Monad
import Control.Monad.Primitive
import Data.Coerce
import Data.Mutable.Instances
import Data.Mutable.Internal
import Data.Primitive.MutVar
import GHC.Generics
import qualified Data.Vinyl.XRec as X
-- | Apply a pure function on an immutable value onto a value stored in
-- a mutable reference.
modifyRef
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> (a -> a)
-> m ()
modifyRef v f = copyRef v . f =<< freezeRef v
{-# INLINE modifyRef #-}
-- | 'modifyRef', but forces the result before storing it back in the
-- reference.
modifyRef'
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> (a -> a)
-> m ()
modifyRef' v f = (copyRef v $!) . f =<< freezeRef v
{-# INLINE modifyRef' #-}
-- | Apply a monadic function on an immutable value onto a value stored in
-- a mutable reference. Uses 'copyRef' into the reference after the
-- action is completed.
modifyRefM
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> (a -> m a)
-> m ()
modifyRefM v f = copyRef v =<< f =<< freezeRef v
{-# INLINE modifyRefM #-}
-- | 'modifyRefM', but forces the result before storing it back in the
-- reference.
modifyRefM'
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> (a -> m a)
-> m ()
modifyRefM' v f = (copyRef v $!) =<< f =<< freezeRef v
{-# INLINE modifyRefM' #-}
-- | Apply a pure function on an immutable value onto a value stored in
-- a mutable reference, returning a result value from that function.
updateRef
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> (a -> (a, b))
-> m b
updateRef v f = do
(x, y) <- f <$> freezeRef v
copyRef v x
return y
-- | 'updateRef', but forces the updated value before storing it back in the
-- reference.
updateRef'
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> (a -> (a, b))
-> m b
updateRef' v f = do
(x, y) <- f <$> freezeRef v
x `seq` copyRef v x
return y
-- | Apply a monadic function on an immutable value onto a value stored in
-- a mutable reference, returning a result value from that function. Uses
-- 'copyRef' into the reference after the action is completed.
updateRefM
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> (a -> m (a, b))
-> m b
updateRefM v f = do
(x, y) <- f =<< freezeRef v
copyRef v x
return y
-- | 'updateRefM', but forces the updated value before storing it back in the
-- reference.
updateRefM'
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> (a -> m (a, b))
-> m b
updateRefM' v f = do
(x, y) <- f =<< freezeRef v
x `seq` copyRef v x
return y
-- | A default implementation of 'copyRef' using 'thawRef' and 'moveRef'.
copyRefWhole
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a -- ^ destination to overwrite
-> a -- ^ pure value
-> m ()
copyRefWhole r v = moveRef r =<< thawRef v
{-# INLINE copyRefWhole #-}
-- | A default implementation of 'moveRef' that round-trips through the
-- pure type, using 'freezeRef' and 'copyRef'. It freezes the entire source
-- and then re-copies it into the destination.
moveRefWhole
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a -- ^ destination
-> Ref s a -- ^ source
-> m ()
moveRefWhole r v = copyRef r =<< freezeRef v
{-# INLINE moveRefWhole #-}
-- | A default implementation of 'moveRef' that round-trips through the
-- pure type, using 'freezeRef' and 'thawRef'. It freezes the entire
-- source and then re-copies it into the destination.
cloneRefWhole
:: (Mutable s a, PrimMonad m, PrimState m ~ s)
=> Ref s a
-> m (Ref s a)
cloneRefWhole = thawRef <=< freezeRef
{-# INLINE cloneRefWhole #-}
-- | Newtype wrapper that can provide any type with a 'Mutable' instance,
-- giving it a "non-piecewise" instance. Can be useful for avoiding orphan
-- instances yet still utilizing auto-deriving features, or for overwriting
-- the 'Mutable' instance of other instances.
--
-- For example, let's say you want to auto-derive an instance for your data
-- type:
--
-- @
-- data MyType = MT Int Double OtherType
-- deriving Generic
-- @
--
-- This is possible if all of @MyType@s fields have 'Mutable' instances.
-- However, let's say @OtherType@ comes from an external library that you
-- don't have control over, and so you cannot give it a 'Mutable' instance
-- without incurring an orphan instance.
--
-- One solution is to wrap it in 'VarMut':
--
-- @
-- data MyType = MT Int Double ('VarMut' OtherType)
-- deriving Generic
-- @
--
-- This can then be auto-derived:
--
-- @
-- instance Mutable s MyType where
-- type Ref s MyType = GRef s MyType
-- @
--
-- It can also be used to /override/ a 'Mutable' instance. For example,
-- even if the 'Mutable' instance of @SomeType@ is piecewise-mutable, the
-- 'Mutable' instance of @'VarMut' SomeType@ will be not be piecewise.
--
-- For example, the 'Mutable' instance for 'String' is a mutable linked
-- list, but it might be more efficient to treat it as an atomic value to
-- update all at once. You can use @'VarMut' 'String'@ to get that
-- 'Mutable' instance.
newtype VarMut a = VarMut { getVarMut :: a }
-- | Use a @'VarMut' a@ as if it were an @a@.
instance X.IsoHKD VarMut a where
type HKD VarMut a = a
unHKD = VarMut
toHKD = getVarMut
instance Mutable s (VarMut a) where
type Ref s (VarMut a) = MutVar s (VarMut a)
-- | Similar to 'VarMut', this allows you to overwrite the normal 'Mutable'
-- instance for a type to utilize its 'Traversable' instance instead of its
-- normal instance. It's also useful to provide an instance for an
-- externally defined type without incurring orphan instances.
--
-- For example, the instance of @'Mutable' ('TraverseMut' [] a)@ is
-- a normal list of mutable references, instead of a full-on mutable linked
-- list.
newtype TraverseMut f a = TraverseMut { getTraverseMut :: f a }
deriving (Show, Eq, Ord, Generic, Functor, Foldable, Traversable)
-- | Use a @'TraverseMut' f a@ as if it were an @f a@
instance X.IsoHKD (TraverseMut f) a where
type HKD (TraverseMut f) a = f a
unHKD = TraverseMut
toHKD = getTraverseMut
instance (Traversable f, Mutable s a) => Mutable s (TraverseMut f a) where
type Ref s (TraverseMut f a) = TraverseRef s (TraverseMut f) a
-- | Similar to 'VarMut', this allows you to overwrite the normal 'Mutable'
-- instance of a type to utilize a coercible type's 'Mutable' instance
-- instead of its normal instance. It's also useful to provide an instance for
-- an externally defined type without incurring orphan instances.
--
-- For example, if an external library provides
--
-- @
-- newtype DoubleVec = DV (Vector Double)
-- @
--
-- and you want to use it following 'V.Vector's 'Mutable' instance (via
-- 'MV.MVector'), but you don't want to write an orphan instance like
--
-- @
-- instance Mutable s DoubleVec where
-- type 'Ref' s DoubleVec = 'CoerceRef' s DoubleVec (Vector Double)
-- @
--
-- then you can instead use @'CoerceMut' DoubleVec (Vector Double)@ as the
-- data type. This wrapped type /does/ use the inderlying 'Mutable'
-- insatnce for 'V.Vector'.
newtype CoerceMut s a = CoerceMut { getCoerceMut :: s }
-- | Use a @'CoerceMut' s a@ as if it were an @s@
instance X.IsoHKD (CoerceMut s) a where
type HKD (CoerceMut s) a = s
unHKD = CoerceMut
toHKD = getCoerceMut
instance (Mutable s a, Coercible s a) => Mutable s (CoerceMut s a) where
type Ref s (CoerceMut s a) = CoerceRef s (CoerceMut s a) a
-- | Similar to 'VarMut', this allows you to overwrite the normal 'Mutable'
-- instance of a type to make it /immutable/.
--
-- For example, let's say you have a type, with the automatically derived
-- generic instance of 'Mutable':
--
-- @
-- data MyType = MT
-- { mtX :: Int
-- , mtY :: Vector Double
-- , mtZ :: String
-- }
-- deriving Generic
--
-- instance Mutable s MyType where
-- type Ref s MyType = GRef s MyType
-- @
--
-- This basically uses three mutable references: the 'Int', the @'V.Vector'
-- Double@, and the 'String'. However, you might want the 'Mutable'
-- instance of @MyType@ to be /immutable/ 'String' field, and so it cannot
-- be updated at all even when thawed. To do that, you can instead have:
--
-- @
-- data MyType = MT
-- { mtX :: Int
-- , mtY :: Vector Double
-- , mtZ :: 'Immutable' String
-- }
-- deriving Generic
--
-- instance Mutable s MyType where
-- type Ref s MyType = GRef s MyType
-- @
--
-- which has that behavior. The 'Int' and the 'V.Vector' will be mutable
-- within @'Ref' s MyType@, but not the 'String'.
newtype Immutable s a = Immutable { getImmutable :: a }
-- | Use an @'Immutable' a@ as if it were an @a@
instance X.IsoHKD (Immutable s) a where
type HKD (Immutable s) a = a
unHKD = Immutable
toHKD = getImmutable
instance Mutable s (Immutable s a) where
type Ref s (Immutable s a) = ImmutableRef s (Immutable s a)