{-# 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 |]