-----------------------------------------------------------------------------
-- Strict State Thread module
-- 
-- This library provides support for strict state threads, as described
-- in the PLDI '94 paper by John Launchbury and Simon Peyton Jones.
-- In addition to the monad ST, it also provides mutable variables STRef
-- and mutable arrays STArray.
--
-- Suitable for use with Hugs 98.
-----------------------------------------------------------------------------

module Hugs.ST 
	( ST(..)
	, runST
	, unsafeRunST
	, RealWorld
	, stToIO
	, unsafeIOToST

	, STRef
	  -- instance Eq (STRef s a)
	, newSTRef
	, readSTRef
	, writeSTRef 

        , STArray
          -- instance Eq (STArray s ix elt)
        , newSTArray
        , boundsSTArray
        , readSTArray
        , writeSTArray
        , thawSTArray
        , freezeSTArray
        , unsafeFreezeSTArray

	, unsafeReadSTArray
	, unsafeWriteSTArray
	) where

import Hugs.Prelude(IO(..))
import Hugs.Array(Array,Ix(index,rangeSize),bounds,elems)
import Hugs.IOExts(unsafePerformIO)
import Control.Monad   

-----------------------------------------------------------------------------

-- The ST representation generalizes that of IO (cf. Hugs.Prelude),
-- so it can use IO primitives that manipulate local state.

newtype ST s a = ST (forall r. (a -> r) -> r)

data RealWorld = RealWorld

primitive thenStrictST "primbindIO" :: ST s a -> (a -> ST s b) -> ST s b
primitive returnST     "primretIO"  :: a -> ST s a

unST                :: ST s a -> (a -> r) -> r
unST (ST f)          = f

runST               :: (forall s. ST s a) -> a
runST m              = unST m id

unsafeRunST         :: ST s a -> a
unsafeRunST m        = unST m id

stToIO              :: ST RealWorld a -> IO a
stToIO (ST f)        = IO f

unsafeIOToST        :: IO a -> ST s a
unsafeIOToST         = unsafePerformIO . liftM returnST

instance Functor (ST s) where
    fmap = liftM

instance Monad (ST s) where
    (>>=)  = thenStrictST
    return = returnST

-----------------------------------------------------------------------------

data STRef s a   -- implemented as an internal primitive

primitive newSTRef   "newRef"     :: a -> ST s (STRef s a)
primitive readSTRef  "getRef"     :: STRef s a -> ST s a
primitive writeSTRef "setRef"     :: STRef s a -> a -> ST s ()
primitive eqSTRef    "eqRef"      :: STRef s a -> STRef s a -> Bool

instance Eq (STRef s a) where (==) = eqSTRef

-----------------------------------------------------------------------------

data STArray s ix elt -- implemented as an internal primitive

newSTArray          :: Ix ix => (ix,ix) -> elt -> ST s (STArray s ix elt)
boundsSTArray       :: Ix ix => STArray s ix elt -> (ix, ix)
readSTArray         :: Ix ix => STArray s ix elt -> ix -> ST s elt
writeSTArray        :: Ix ix => STArray s ix elt -> ix -> elt -> ST s ()
thawSTArray         :: Ix ix => Array ix elt -> ST s (STArray s ix elt)
freezeSTArray       :: Ix ix => STArray s ix elt -> ST s (Array ix elt)
unsafeFreezeSTArray :: Ix ix => STArray s ix elt -> ST s (Array ix elt)

unsafeReadSTArray   :: Ix i => STArray s i e -> Int -> ST s e
unsafeReadSTArray    = primReadArr

unsafeWriteSTArray  :: Ix i => STArray s i e -> Int -> e -> ST s ()
unsafeWriteSTArray   = primWriteArr

newSTArray bs e      = primNewArr bs (rangeSize bs) e
boundsSTArray a      = primBounds a
readSTArray a i      = unsafeReadSTArray a (index (boundsSTArray a) i)
writeSTArray a i e   = unsafeWriteSTArray a (index (boundsSTArray a) i) e
thawSTArray arr      = do
		       stArr <- newSTArray (bounds arr) err
		       sequence_ (zipWith (unsafeWriteSTArray stArr)
						[0..] (elems arr))
		       return stArr
 where
  err = error "thawArray: element not overwritten" -- shouldnae happen
freezeSTArray a      = primFreeze a
unsafeFreezeSTArray  = freezeSTArray  -- not as fast as GHC

instance Eq (STArray s ix elt) where
  (==) = eqSTArray

primitive primNewArr   "IONewArr"
          :: (a,a) -> Int -> b -> ST s (STArray s a b)
primitive primReadArr  "IOReadArr"
          :: STArray s a b -> Int -> ST s b
primitive primWriteArr "IOWriteArr"
          :: STArray s a b -> Int -> b -> ST s ()
primitive primFreeze   "IOFreeze"
          :: STArray s a b -> ST s (Array a b)
primitive primBounds   "IOBounds"
          :: STArray s a b -> (a,a)
primitive eqSTArray    "IOArrEq"
          :: STArray s a b -> STArray s a b -> Bool

-----------------------------------------------------------------------------
