{-# OPTIONS -fglasgow-exts #-}
{-# OPTIONS -fallow-undecidable-instances #-}
{-# OPTIONS -fallow-overlapping-instances #-}
{-# OPTIONS -fallow-incoherent-instances #-}

module MonadicRegions where

{-
  NOTE ABOUT EXTENSIONS

The overlapping instance extension is used ONLY for the type equality
testing. To be more precise, we use a subset of that extension
as implemented in GHC 6.3 and above. Given a set of instances
  class C a where op:: a -> Int
  instance C a where ...
  instance C Bool where ...
  data W  = forall a. W a
the expression
  case W () of W x -> op x 
is well-typed with the "instance C a" chosen. In the context of TypeEq,
that behavior guarantees that a quantified variable is equal only to itself
and nothing else. So, the use of overlapping instances in this code
is well-defined and sound.

The existence of this remarkable feature has been pointed out by Simon
Peyton-Jones, who also implemented it.

As it turns out, with -fallow-incoherent-instances, the code runs
(and gives errors, where expected) even with GHC 6.2.1

So, this code tested on GHC 6.2.1 and GHC ghc-6.3.20041106 snapshot
And GHC 6.4
-}

import Control.Monad.ST
import Data.STRef

testr1 = runST (do a <- (runRGN (do return ()))
		   return a)

testr2 = let vf = runRGN (do r1 <- getRGN
			     a  <- newRGNVar (1::Int) r1
			     readRGNVar a)
	 in runST vf

-- try to allocate RGNVar in a bad scope
-- It gives an error, as expected
{-
testrw = let vf = runRGN (do r1 <- badgetRGN
			     a  <- newRGNVar (1::Int) r1
			     readRGNVar a)
	 in runST vf
-}

testr3 = let vf = runRGN (do r1 <- getRGN
			     a  <- newRGNVar (1::Int) r1
			     c <- newRGN (do v <- readRGNVar a
					     r2 <- getRGN
					     b <- newRGNVar (2::Int) r2
					     v' <- readRGNVar b
					     newRGNVar (v+v') r1)
			     readRGNVar c)
	 in runST vf

-- Note the inferred type of gpair!
gpair v w = do
	    a <- readRGNVar v
	    b <- readRGNVar w
	    l <- getRGN
	    newRGNVar (a,b) l

testr4 = let vf = runRGN (do r1 <- getRGN
			     v  <- newRGNVar (1::Int) r1
			     newRGN (do r2 <- getRGN
				        w  <- newRGNVar (2::Int) r2
				        newRGN (do
						p1 <- gpair v w 
						p2 <- gpair w v
						a1 <- readRGNVar p1
					        a2 <- readRGNVar p2
					        return (a1,a2))))
	 in runST vf

-- Indeed, gpair is polymorphic to the desired extent...
testr5 = let vf = runRGN (do r1 <- getRGN
			     v  <- newRGNVar (1::Int) r1
			     newRGN (do p <- gpair v v 
				        readRGNVar p))
	 in runST vf

------------------------------------------------------------------------
-- The RGN monad
-- RGN r p s a
-- r is the region's unique label
-- p is the HList of region's labels in scope (it is treated like a SET)
-- s is the thread id of the underlying ST monad, in term of which
--      we emulate everything
-- a is the type of the value

newtype RGN r p s a = RGN (ST s a)

unRGN (RGN a) = a

instance Monad (RGN r p s) where
    return = RGN . return
    (RGN m) >>= f = RGN $ m >>= (unRGN . f)

runRGN::(forall r. RGN r (HCons r HNil) s a) -> ST s a
runRGN = unRGN

newRGN::(forall r'. RGN r' (HCons r' p) s a) -> RGN r p s a
newRGN = RGN . unRGN

-- Get our own label
data RGNLab r = RGNLab
getRGN:: RGN r p s (RGNLab r)
getRGN = return RGNLab

-- used for testing
badgetRGN:: RGN r p s (RGNLab r')
badgetRGN = return RGNLab


newtype RGNVar r s a = RGNVar (STRef s a)
--newRGNVar:: a -> (RGNLab rv) -> RGN r p s (RGNVar rv s a)
newRGNVar:: HOccurs rv p => a -> (RGNLab rv) -> RGN r p s (RGNVar rv s a)
newRGNVar v _ = RGN $ newSTRef v >>= return . RGNVar

readRGNVar::HOccurs rv p => RGNVar rv s a -> RGN r p s a
--readRGNVar::RGNVar rv s a -> RGN r p s a
readRGNVar (RGNVar r) = RGN $ readSTRef r


------------------------------------------------------------------------

-- Testing for occurrence of a type in a HList
-- The constraint `HOccurs a l' holds if type `a' occurs in the type list l

data HCons a b = HCons a b
data HNil = HNil

class HOccurs a l
instance HOccurs a  (HCons a l)
instance HOccurs a l => HOccurs a (HCons a' l)


-- some tests
test1:: HOccurs a l => l -> a; test1 = undefined
_ = test1 (HCons True (HCons () HNil)) :: Bool
_ = test1 (HCons True (HCons () HNil)) :: ()
-- The latter should be a type error
-- _ = test1 (HCons True (HCons () HNil)) :: Int


data W  = forall a. W a
test2::HOccurs a l => l -> a -> (); test2 _ _ = ()

_ = case W True of W x -> test2 (HCons x (HCons 'a' HNil)) 'b'
_ = case W True of W x -> test2 (HCons 'z' (HCons x HNil)) 'b'
_ = case W True of W x -> test2 (HCons () (HCons x (HCons 'a' HNil))) 'b'
_ = case W True of W x -> test2 (HCons () (HCons x (HCons 'a' HNil))) ()
_ = case W True of W x -> test2 (HCons x (HCons 'a' HNil)) x
_ = case W True of
      W x -> 
	  case W 'a' of W y -> 
			    test2 (HCons x (HCons 'a' (HCons y HNil))) x

_ = case W True of
      W x -> 
	  case W 'a' of W y -> 
			    test2 (HCons x (HCons 'a' (HCons y HNil))) y

{-
  But the following is error:
    No instance for (HOccurs a HNil)
      arising from use of `test2' at /tmp/m.hs:111:28-32

_ = case W True of
      W x -> 
	  case W 'a' of W y -> 
			    test2 (HCons x (HCons 'a' (HCons x HNil))) y

-}

