large-anon-0.1.0.0: src/Data/Record/Anon/Internal/Util/StrictArray.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Record.Anon.Internal.Util.StrictArray (
StrictArray -- opaque
-- * Reads
, (!)
-- * Conversion
, fromList
, fromListN
, fromLazy
, toLazy
-- * Non-monadic combinators
, (//)
, update
, backpermute
, zipWith
-- * Monadic combinators
, mapM
, zipWithM
) where
import Prelude hiding (mapM, zipWith)
import Control.Monad (forM_)
import Data.Primitive.SmallArray
import qualified Control.Monad as Monad
import qualified Data.Foldable as Foldable
{-------------------------------------------------------------------------------
Definition
-------------------------------------------------------------------------------}
-- | Strict vector
--
-- Implemented as a wrapper around a 'SmallArray'.
--
-- NOTE: None of the operations on 'Vector' do any bounds checking.
--
-- NOTE: 'Vector' is implemented as a newtype around 'SmallArray', which in turn
-- is defined as
--
-- > data SmallArray a = SmallArray (SmallArray# a)
--
-- Furthermore, 'Canonical' is a newtype around 'Vector', which is then used in
-- 'Record' as
--
-- > data Record (f :: k -> Type) (r :: Row k) = Record {
-- > recordCanon :: {-# UNPACK #-} !(Canonical f)
-- > , ..
-- > }
--
-- This means that 'Record' will have /direct/ access (no pointers) to the
-- 'SmallArray#'.
newtype StrictArray a = WrapLazy { unwrapLazy :: SmallArray a }
deriving newtype (Show, Eq, Foldable, Semigroup, Monoid)
{-------------------------------------------------------------------------------
Reads
-------------------------------------------------------------------------------}
(!) :: StrictArray a -> Int -> a
(!) = indexSmallArray . unwrapLazy
{-------------------------------------------------------------------------------
Conversion
-------------------------------------------------------------------------------}
fromList :: [a] -> StrictArray a
fromList as = fromListN (length as) as
fromListN :: Int -> [a] -> StrictArray a
fromListN n as = WrapLazy $ runSmallArray $ do
r <- newSmallArray n undefined
forM_ (zip [0..] as) $ \(i, !a) ->
writeSmallArray r i a
return r
fromLazy :: forall a. SmallArray a -> StrictArray a
fromLazy v = go 0
where
go :: Int -> StrictArray a
go i
| i < sizeofSmallArray v
= let !_a = indexSmallArray v i in go (succ i)
| otherwise
= WrapLazy v
toLazy :: StrictArray a -> SmallArray a
toLazy = unwrapLazy
{-------------------------------------------------------------------------------
Non-monadic combinators
-------------------------------------------------------------------------------}
instance Functor StrictArray where
fmap f (WrapLazy as) = WrapLazy $ runSmallArray $ do
r <- newSmallArray newSize undefined
forArrayM_ as $ \i a -> writeSmallArray r i $! f a
return r
where
newSize :: Int
newSize = sizeofSmallArray as
(//) :: StrictArray a -> [(Int, a)] -> StrictArray a
(//) (WrapLazy as) as' = WrapLazy $ runSmallArray $ do
r <- thawSmallArray as 0 newSize
forM_ as' $ \(i, !a) -> writeSmallArray r i a
return r
where
newSize :: Int
newSize = sizeofSmallArray as
update :: StrictArray a -> StrictArray (Int, a) -> StrictArray a
update (WrapLazy as) (WrapLazy as') = WrapLazy $ runSmallArray $ do
r <- thawSmallArray as 0 newSize
forArrayM_ as' $ \_i (j, !a) -> writeSmallArray r j a
return r
where
newSize :: Int
newSize = sizeofSmallArray as
backpermute :: StrictArray a -> StrictArray Int -> StrictArray a
backpermute (WrapLazy as) (WrapLazy is) = WrapLazy $ runSmallArray $ do
r <- newSmallArray newSize undefined
forArrayM_ is $ \i j -> writeSmallArray r i $! indexSmallArray as j
return r
where
newSize :: Int
newSize = length is
zipWith :: (a -> b -> c) -> StrictArray a -> StrictArray b -> StrictArray c
zipWith f (WrapLazy as) (WrapLazy bs) = WrapLazy $ runSmallArray $ do
r <- newSmallArray newSize undefined
forM_ [0 .. newSize - 1] $ \i -> do
let !c = f (indexSmallArray as i) (indexSmallArray bs i)
writeSmallArray r i c
return r
where
newSize :: Int
newSize = min (sizeofSmallArray as) (sizeofSmallArray bs)
{-------------------------------------------------------------------------------
Applicative combinators
NOTE: The monadic combinators here do two traversals, first collecting all
elements of the vector in memory, and then constructing the new vector. The
alternative is to use 'traverseSmallArrayP', but it is only sound with
certain monads. Since this restriction would leak out to users of the library
(through the monadic combinators on 'Record'), we prefer to avoid it.
-------------------------------------------------------------------------------}
mapM :: forall m a b.
Applicative m
=> (a -> m b) -> StrictArray a -> m (StrictArray b)
mapM f (WrapLazy as) =
fromListN newSize <$>
traverse f (Foldable.toList as)
where
newSize :: Int
newSize = sizeofSmallArray as
zipWithM ::
Applicative m
=> (a -> b -> m c) -> StrictArray a -> StrictArray b -> m (StrictArray c)
zipWithM f (WrapLazy as) (WrapLazy bs) = do
fromListN newSize <$>
Monad.zipWithM f (Foldable.toList as) (Foldable.toList bs)
where
newSize :: Int
newSize = min (sizeofSmallArray as) (sizeofSmallArray bs)
{-------------------------------------------------------------------------------
Internal auxiliary
-------------------------------------------------------------------------------}
forArrayM_ :: forall m a. Monad m => SmallArray a -> (Int -> a -> m ()) -> m ()
forArrayM_ arr f = go 0
where
go :: Int -> m ()
go i
| i < sizeofSmallArray arr
= f i (indexSmallArray arr i) >> go (succ i)
| otherwise
= return ()