{-# LANGUAGE TypeOperators
            ,ScopedTypeVariables
            ,PostfixOperators
            ,NoMonomorphismRestriction
            ,BangPatterns
  #-}
{-# OPTIONS_GHC -fno-cse #-}
module Data.OI (
  OI
 ,(:->)
 ,iooi
 ,run
 ,(=:)
 ,(?)
 ,(#)
 ,idA
 ,(<.>)
 ,arrA
 ,firstA
 ,deTuple
 ,deList
 ,(<|)
 ,zipWithOI
 ,zipWithOI'
 ,mapOI
 ) where

import Control.Applicative
import Control.Concurrent.MVar
import Control.Parallel
import Control.Comonad
import System.IO.Unsafe

data OI a = OI { variable :: LeftValueOf (IO a), value :: a }
type a :-> b = OI a -> b

(=:)         ::  a -> a :-> a
x =: OI v y  =   assign ix v `pseq` y
                 where ix = return x

(?)           ::  a :-> a
(?) (OI _ x)  =   x

(#)    ::  a -> OI a
(#) x  =   OI { variable = reference (return x), value = x }

--

instance Functor OI where
  fmap f x = (#) (f (x?))

instance Monad OI where
  return = (#)
  x >>= f = f (x?)

instance Applicative OI where
  pure = (#)
  f <*> x = (#)((f?)(x?))

instance Extend OI where
  duplicate = (#)

instance Comonad OI where
  extract = (?)

--

idA :: a :-> a
idA = (?)

(<.>) :: (b :-> c) -> (a :-> b) -> (a :-> c)
f <.> g = f . (#) . g

--

arrA :: (a -> b) -> (a :-> b)
arrA f = f . (?)

firstA :: (a :-> b) -> (a,c) :-> (b,c)
firstA f ac = case deTuple ac of (x,z) -> (f x, (z?))

--

deTuple :: (a,b) :-> (OI a,OI b)
deTuple (OI vxy ~(x,y)) = assign io vxy `pseq` (OI vx x, OI vy y)
 where
  vx     = new ()
  vy     = new ()
  io     = (,) <$> unsafeInterleaveIO (dereference vx) <*> unsafeInterleaveIO (dereference vy)

deList :: [a] :-> Maybe (OI a, OI [a])
deList (OI vxxs xxs)
 = assign io vxxs
   `pseq` case xxs of
      x:xs -> Just (OI vx x, OI vxs xs)
      _    -> Nothing
   where
     vx  = new ()
     vxs = new ()
     io  = (:) <$> (unsafeInterleaveIO (dereference vx)) <*> (unsafeInterleaveIO (dereference vxs))

--

(<|)  ::  (b -> c :-> d) -> (a :-> b) -> (a,c) :-> d
(f <| g) ac = case deTuple ac of (a,c) -> f (g a) c

mapOI :: (a :-> b) -> [a] :-> [b]
mapOI f xxs = case deList xxs of
  Just (x,xs) -> f x : mapOI f xs
  _           -> []

zipWithOI :: (a -> b :-> c) -> [a] -> [b] :-> [c]
zipWithOI _ [] _  = []
zipWithOI f (x:xs) yys = case deList yys of
  Just (y,ys) -> f x y : zipWithOI f xs ys
  _           -> []

zipWithOI' :: (a -> b :-> c) -> [a] -> [b] :-> (OI [b],[c])
zipWithOI' _ []     yys = (yys,[])
zipWithOI' f (x:xs) yys = case deList yys of
  Just (y,ys) -> case zipWithOI' f xs ys of ~(rs,zs) -> (rs,f x y : zs)
  _           -> (yys,[])

--

iooi            ::  IO a -> (a :-> a)
iooi io (OI vix x)  =   assign io vix `pseq` x

run        ::  (a :-> b) -> IO b
run pmain  =   do { vx <- unsafeInterleaveIO newEmptyMVar
                  ; x  <- unsafeInterleaveIO (dereference vx)
                  ; return $! pmain (OI vx x)
                  }

--

type LeftValueOf = MVar

{-# INLINE new #-}
new :: () -> LeftValueOf a
new _ = unsafePerformIO $ newEmptyMVar

{-# INLINE reference #-}
reference :: a -> LeftValueOf a
reference = unsafePerformIO . newMVar

{-# INLINE dereference #-}
dereference :: LeftValueOf a -> a
dereference = unsafePerformIO . readMVar

{-# INLINE assign #-}
assign :: a -> LeftValueOf a -> a
assign !x v = unsafePerformIO 
            $ do { s <- tryPutMVar v x
                 ; if s then return x else readMVar v
                 }