content: {-# 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.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 |]
  