FastPush-0.1.0.2: src/Control/Monad/Push.hs
-- Hybrid ReaderT/StateT style
-- 100M Ints in ~3.1 seconds
-- 2.95G allocated. Seems to match minimal ST loop
{-# LANGUAGE TypeApplications, RankNTypes, MultiParamTypeClasses, GeneralizedNewtypeDeriving, DeriveFunctor, FlexibleInstances, FlexibleContexts, ScopedTypeVariables #-}
module Control.Monad.Push where
import Control.Monad.ST (ST, runST)
import qualified Data.Vector as V
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Generic.Mutable as VGM
import Data.STRef.Strict (STRef, readSTRef, writeSTRef, newSTRef)
import Control.Monad.Push.Class (MonadPush, push)
-- | The internal return type of a push action.
-- The Int value is the new vector used length.
data Res a = Res !Int
!a deriving Functor
-- | A monad that lets you push things onto a stack.
newtype Push v p a = Push (forall s . Int -> (STRef s (v s p)) -> (ST s (Res a))) deriving (Functor)
instance Applicative (Push v p) where
{-# INLINE pure #-}
pure a = Push $ \u _ -> (return (Res u a))
{-# INLINE (<*>) #-}
(Push f) <*> (Push g) = Push $ \u v -> f u v >>= (\(Res u' o1) -> g u' v >>= (\(Res u'' o2) -> return (Res u'' (o1 o2))))
instance Monad (Push v p) where
{-# INLINE return #-}
return = pure
{-# INLINE (>>=) #-}
Push g >>= f = Push $ \u v -> ((g u v) >>= (\(Res u' x) -> let Push h = f x in h u' v))
{-# INLINE (>>) #-}
Push g >> Push h = Push $ \u v -> g u v >>= (\(Res u' _) -> h u' v)
instance VGM.MVector v p => MonadPush p (Push v p) where
{-# INLINE push #-}
push a = Push $ \used vec' -> do
vec <- readSTRef vec'
if (VGM.length vec == used)
then do
bigger <- VGM.grow vec used -- Double the length
VGM.write bigger used a
writeSTRef vec' bigger
else do
VGM.write vec used a
return $ Res (used+1) ()
-- | Run the Push monad. Get the return value and the output stack.
{-# INLINE runPush #-}
runPush :: VG.Vector v p => Push (VG.Mutable v) p a -> (a, v p)
runPush (Push action) = runST $ do
initial <- VGM.new 1
vecRef <- newSTRef initial
(Res used out) <- action 0 vecRef
final <- readSTRef vecRef
vec <- VG.freeze (VGM.slice 0 used final)
return (out, vec)
-- | Specialized to Unboxed vectors.
{-# INLINE runPushU #-}
runPushU :: forall p a . VU.Unbox p => Push (VU.MVector) p a -> (a, VU.Vector p)
runPushU = runPush
-- | Specialized to standard Boxed vectors.
{-# INLINE runPushB #-}
runPushB :: forall p a . Push (V.MVector) p a -> (a, V.Vector p)
runPushB = runPush
-- | Specialized to Storable vectors.
{-# INLINE runPushS #-}
runPushS :: forall p a . VS.Storable p => Push (VS.MVector) p a -> (a, VS.Vector p)
runPushS = runPush