-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Monad.Queue.Allison
-- Copyright   :  (c) Leon P Smith 2009
-- License     :  BSD3
--
-- Maintainer  :  leon at melding-monads dot com
-- Stability   :  experimental
-- Portability :  portable
--
-- A library implementation of corecursive queues,  see
-- /Circular Programs and Self-Referential Structures/ by Lloyd Allison,
-- /Software Practice and Experience/, 19(2), pp.99-109, Feb 1989
--
-- <http://www.csse.monash.edu.au/~lloyd/tildeFP/1989SPE/>
--
-- For an explanation of the library implementation, see
-- /Lloyd Allison's Corecursive Queue:  Why Continuations Matter/
-- by Leon P Smith,  in /The Monad Reader/ issue 14.
--
-----------------------------------------------------------------------------

{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE FunctionalDependencies     #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE RankNTypes                 #-}

module  Control.Monad.Queue.Allison
     (  Q()
     ,  LenType
     ,  enQ
     ,  peekQ
     ,  peekQn
     ,  peekQs
     ,  deQ
     ,  deQs
     ,  deQ_break
     ,  lenQ
     ,  lenQ_
     ,  runQueue
     ,  callCC
     )  where

import qualified Control.Monad.Queue.Class as Class
import Control.Monad.Queue.Util
import Data.List(genericIndex, genericTake, genericSplitAt)

type QSt e = LenType -> [e] -> [e]

newtype Q e a = Q { unQ :: (a -> QSt e) -> QSt e }

instance Monad (Q e) where
  return a  = Q (\k -> k a)
  m >>= f   = Q (\k -> unQ m (\a -> unQ (f a) k))

callCC :: ((a -> forall b. Q e b) -> Q e a) -> Q e a
callCC f = Q (\k -> unQ (f (\a -> Q (\_ -> k a))) k)

enQ :: e -> Q e ()
enQ e = Q (\k n q -> e : (k () $! n+1) q)

deQ :: Q e (Maybe e)
deQ   = Q delta
  where
    delta k n q
       | n <= 0    = k Nothing n q
       | otherwise = case q of
                      [] -> error "Control.Monad.Queue.Allison.deQ: empty list"
                      (e:q') -> (k (Just e) $! n-1) q'

deQ_break :: Q e e
deQ_break =  Q delta
  where
    delta k n q
       | n <= 0    = []
       | otherwise = case q of
                      [] -> error "Control.Monad.Queue.Allison.deQ_break: empty list"
                      (e:q') -> (k e $! n-1) q'

deQs :: Integral len => len -> Q e [e]
deQs i = Q delta
  where
    delta k n q
       = let i' = min (fromIntegral i) n
             (res,q') = genericSplitAt i' q
          in (k res $! n-i') q'

peekQ :: Q e (Maybe e)
peekQ = Q delta
  where
    delta k n q
       | n <= 0    = k Nothing n q
       | otherwise = case q of
                      [] -> error "Control.Monad.Queue.Allison.peekQ: empty list"
                      (e:_q') -> k (Just e) n q

peekQn :: (Integral index) => index -> Q e (Maybe e)
peekQn i_ = Q delta
  where
    i = fromIntegral i_

    delta k n q
       | n < i     = k Nothing n q
       | otherwise = k (Just (genericIndex q i)) n q

peekQs :: (Integral len) => len -> Q e [e]
peekQs i_ = Q delta
  where
    i = fromIntegral i_
    delta k n q = k (genericTake (min i n) q) n q

lenQ_ :: Q e LenType
lenQ_ = Q (\k n q -> k n n q)

lenQ  :: Integral len => Q e len
lenQ  = Q (\k n q -> k (fromIntegral n) n q)

runQueue :: Q e a -> [e]
runQueue m = q
  where
    q = unQ m (\_ _ _ -> []) 0 q

instance Class.MonadQueue e (Q e) where
  enQ     = enQ
  peekQ   = peekQ
  peekQs  = peekQs
  peekQn  = peekQn
  deQ     = deQ
  deQs    = deQs
  lenQ    = lenQ