subhask-0.1.0.0: src/SubHask/Mutable.hs
{-# LANGUAGE NoAutoDeriveTypeable #-}
-- | In the SubHask library, every type has both a mutable and immutable version.
-- Normally we work with the immutable version;
-- however, certain algorithms require the mutable version for efficiency.
-- This module defines the interface to the mutable types.
module SubHask.Mutable
( Mutable
, IsMutable (..)
, immutable2mutable
, mutable2immutable
, unsafeRunMutableProperty
, mkMutable
-- ** Primitive types
, PrimBase
, PrimState
-- ** Internal
-- | These exports should never be used directly.
-- They are required by the "mkMutable" TH function.
, PrimRef
, readPrimRef
, writePrimRef
, newPrimRef
, helper_liftM
)
where
import SubHask.Internal.Prelude
import SubHask.TemplateHaskell.Deriving
import SubHask.TemplateHaskell.Mutable
import Prelude (($),(.))
import Control.Monad
import Control.Monad.Primitive
import Control.Monad.ST
import Data.Primitive
import Data.PrimRef
import System.IO.Unsafe
--------------------------------------------------------------------------------
-- | The mutable version of an immutable data type.
-- This is equivalent to the "PrimRef" type, which generalizes "STRef" and "IORef".
--
-- Unlike "PrimRef", "Mutable" is implemented using a data family.
-- This means that data types can provide more efficient implementations.
-- The canonical example is "Vector".
-- Vectors in standard Haskell use a different interface than the standard "PrimRef".
-- This requires the programmer learn multiple interfaces, and prevents the programmer from reusing code.
-- Very un-Haskelly.
-- This implementation of mutability gives a consistent interface for all data types.
data family Mutable (m :: * -> *) a
instance (Show a, IsMutable a, PrimBase m) => Show (Mutable m a) where
show mx = unsafePerformIO $ unsafePrimToIO $ do
x <- freeze mx
return $ "Mutable ("++show x++")"
instance (IsMutable a, PrimBase m, Arbitrary a) => Arbitrary (Mutable m a) where
arbitrary = do
a <- arbitrary
return $ unsafePerformIO $ unsafePrimToIO $ thaw a
-- | A Simple default implementation for mutable operations.
{-# INLINE immutable2mutable #-}
immutable2mutable :: IsMutable a => (a -> b -> a) -> (PrimBase m => Mutable m a -> b -> m ())
immutable2mutable f ma b = do
a <- freeze ma
write ma (f a b)
-- | A Simple default implementation for immutable operations.
{-# INLINE mutable2immutable #-}
mutable2immutable :: IsMutable a => (forall m. PrimBase m => Mutable m a -> b -> m ()) -> a -> b -> a
mutable2immutable f a b = runST ( do
ma <- thaw a
f ma b
unsafeFreeze ma
)
-- | This function should only be used from within quickcheck properties.
-- All other uses are unsafe.
unsafeRunMutableProperty :: PrimBase m => m a -> a
unsafeRunMutableProperty = unsafePerformIO . unsafePrimToIO
-- | This class implements conversion between mutable and immutable data types.
-- It is the equivalent of the functions provided in "Contol.Monad.Primitive",
-- but we use the names of from the "Data.Vector" interface because they are simpler and more intuitive.
--
-- Every data type is an instance of this class using a default implementation based on "PrimRef"s.
-- We use OverlappingInstances to allow some instances to provide more efficient implementations.
-- We require that any overlapping instance be semantically equivalent to prevent unsafe behavior.
-- The use of OverlappingInstances should only affect you if your creating your own specialized instances of the class.
-- You shouldn't have to do this unless you are very concerned about performance on a complex type.
--
-- FIXME:
-- It's disappointing that we still require this class, the "Primitive" class, and the "Storable" class.
-- Can these all be unified?
class IsMutable a where
-- | Convert a mutable object into an immutable one.
-- The implementation is guaranteed to copy the object within memory.
-- The overhead is linear with the size of the object.
freeze :: PrimBase m => Mutable m a -> m a
-- | Convert an immutable object into a mutable one
-- The implementation is guaranteed to copy the object within memory.
-- The overhead is linear with the size of the object.
thaw :: PrimBase m => a -> m (Mutable m a)
-- | Assigns the value of the mutable variable to the immutable one.
write :: PrimBase m => Mutable m a -> a -> m ()
-- | Return a copy of the mutable object.
-- Changes to the copy do not update in the original, and vice-versa.
copy :: PrimBase m => Mutable m a -> m (Mutable m a)
copy ma = do
a <- unsafeFreeze ma
thaw a
-- | Like "freeze", but much faster on some types
-- because the implementation is not required to perform a memory copy.
--
-- WARNING:
-- You must not modify the mutable variable after calling unsafeFreeze.
-- This might change the value of the immutable variable.
-- This breaks referential transparency and is very bad.
unsafeFreeze :: PrimBase m => Mutable m a -> m a
unsafeFreeze = freeze
-- | Like "thaw", but much faster on some types
-- because the implementation is not required to perform a memory copy.
--
-- WARNING:
-- You must not access the immutable variable after calling unsafeThaw.
-- The contents of this variable might have changed arbitrarily.
-- This breaks referential transparency and is very bad.
unsafeThaw :: PrimBase m => a -> m (Mutable m a)
unsafeThaw = thaw
--------------------------------------------------------------------------------
mkMutable [t| Int |]
mkMutable [t| Integer |]
mkMutable [t| Rational |]
mkMutable [t| Float |]
mkMutable [t| Double |]
mkMutable [t| Bool |]
mkMutable [t| forall a. [a] |]
mkMutable [t| () |]
mkMutable [t| forall a b. (a,b) |]
mkMutable [t| forall a b c. (a,b,c) |]
mkMutable [t| forall a b. a -> b |]