mirror of
https://github.com/zap-proto/haskell.git
synced 2026-10-30 15:05:00 +00:00
57 lines
2.3 KiB
Haskell
57 lines
2.3 KiB
Haskell
{-# LANGUAGE DataKinds #-}
|
|
{-# LANGUAGE KindSignatures #-}
|
|
{-# LANGUAGE RankNTypes #-}
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
module Zap.Mutability
|
|
( Mutability (..),
|
|
MaybeMutable (..),
|
|
create,
|
|
createT,
|
|
)
|
|
where
|
|
|
|
import Control.Monad.Primitive (PrimMonad (PrimState))
|
|
import Control.Monad.ST (ST, runST)
|
|
import Data.Kind (Type)
|
|
|
|
-- | 'Mutability' is used as a type parameter (with the DataKinds extension)
|
|
-- to indicate the mutability of some values in this library; 'Const' denotes
|
|
-- an immutable value, while @'Mut' s@ denotes a value that can be mutated
|
|
-- in the scope of the state token @s@.
|
|
data Mutability = Const | Mut Type
|
|
|
|
-- | 'MaybeMutable' relates mutable and immutable versions of a type.
|
|
class MaybeMutable (f :: Mutability -> Type) where
|
|
-- | Convert an immutable value to a mutable one.
|
|
thaw :: (PrimMonad m, PrimState m ~ s) => f 'Const -> m (f ('Mut s))
|
|
|
|
-- | Convert a mutable value to an immutable one.
|
|
freeze :: (PrimMonad m, PrimState m ~ s) => f ('Mut s) -> m (f 'Const)
|
|
|
|
-- | Like 'thaw', except that the caller is responsible for ensuring that
|
|
-- the original value is not subsequently used; doing so may violate
|
|
-- referential transparency.
|
|
--
|
|
-- The default implementation of this is just the same as 'thaw', but
|
|
-- typically an instance will override this with a trivial (unsafe) cast,
|
|
-- hence the obligation described above.
|
|
unsafeThaw :: (PrimMonad m, PrimState m ~ s) => f 'Const -> m (f ('Mut s))
|
|
unsafeThaw = thaw
|
|
|
|
-- | Unsafe version of 'freeze' analagous to 'unsafeThaw'. The caller must
|
|
-- ensure that the original value is not used after this call.
|
|
unsafeFreeze :: (PrimMonad m, PrimState m ~ s) => f ('Mut s) -> m (f 'Const)
|
|
unsafeFreeze = freeze
|
|
|
|
-- | Create and freeze a mutable value, safely, without doing a full copy.
|
|
-- internally, 'create' calls unsafeFreeze, but it cannot be directly used to
|
|
-- violate referential transparency, as the value is not available to the
|
|
-- caller after freezing.
|
|
create :: MaybeMutable f => (forall s. ST s (f ('Mut s))) -> f 'Const
|
|
create st = runST (st >>= unsafeFreeze)
|
|
|
|
-- | Like 'create', but the result is wrapped in an instance of 'Traversable'.
|
|
createT :: (Traversable t, MaybeMutable f) => (forall s. ST s (t (f ('Mut s)))) -> t (f 'Const)
|
|
createT st = runST (st >>= traverse unsafeFreeze)
|