packages feed

co-feldspar-0.1.0.1: src/Feldspar/Storable.hs

{-# language TypeFamilies          #-}
{-# language FlexibleInstances     #-}
{-# language MultiParamTypeClasses #-}
{-# language ScopedTypeVariables   #-}

module Feldspar.Storable where

import Feldspar.Frontend
import Data.Struct

import Data.Proxy

--------------------------------------------------------------------------------
-- * Storable.
--------------------------------------------------------------------------------

class Storable m a
  where
    -- | Memory representation
    type StoreRep m a
    -- | Size of memory representation
    type StoreSize m a

    -- | Creat a fresh memory store. It is usually better to use 'newStore'
    --   instead of this function as it improves type inference.
    newStoreRep :: MonadComp m => proxy a -> StoreSize m a -> m (StoreRep m a)

    -- | Store a value to a fresh memory store. It is usually better to use
    --   'initStore' instead of this function as it improves type inference.
    initStoreRep :: MonadComp m => a -> m (StoreRep m a)

    -- | Read from a memory store. It is usually better to use 'readStore'
    --   instead of this function as it improves type inference.
    readStoreRep :: MonadComp m => StoreRep m a -> m a

    -- | Unsafe freezing of a memory store. It is usually better to use
    --   'unsafeFreezeStore' instead of this function as it improves type
    --   inference.
    unsafeFreezeStoreRep :: MonadComp m => StoreRep m a -> m a

    -- | Write to a memory store. It is usually better to use 'writeStore'
    --   instead of this function as it improves type inference.
    writeStoreRep :: MonadComp m => StoreRep m a -> a -> m ()

instance Storable m ()
  where
    type StoreRep m ()  = ()
    type StoreSize m () = ()
    newStoreRep _ _        = return ()
    initStoreRep _         = return ()
    readStoreRep _         = return ()
    unsafeFreezeStoreRep _ = return ()
    writeStoreRep _ _      = return ()

instance forall m a b . (Storable m a, Storable m b) => Storable m (a,b)
  where
    type StoreRep m (a,b) = (StoreRep m a, StoreRep m b)
    type StoreSize m (a,b) = (StoreSize m a, StoreSize m b)
    newStoreRep _ (a,b)          = (,) <$> newStoreRep (Proxy :: Proxy a) a <*> newStoreRep (Proxy :: Proxy b) b
    initStoreRep (a,b)           = (,) <$> initStoreRep a <*> initStoreRep b
    readStoreRep (la,lb)         = (,) <$> readStoreRep la <*> readStoreRep lb
    unsafeFreezeStoreRep (la,lb) = (,) <$> unsafeFreezeStoreRep la <*> unsafeFreezeStoreRep lb
    writeStoreRep (la,lb) (a,b)  = writeStoreRep la a >> writeStoreRep lb b

--------------------------------------------------------------------------------
-- ** User interface.
--------------------------------------------------------------------------------

-- | Memory for storing values.
newtype Store m a = Store { unStore :: StoreRep m a }

-- | Create a fresh 'Store'.
newStore :: forall a m . (MonadComp m, Storable m a)
  => StoreSize m a
  -> m (Store m a)
newStore = fmap Store . newStoreRep (Proxy :: Proxy a)

-- | Store a value to a fresh 'Store'.
initStore :: (MonadComp m, Storable m a) => a -> m (Store m a)
initStore = fmap Store . initStoreRep

-- | Read from a 'Store'.
readStore :: (MonadComp m, Storable m a) => Store m a -> m a
readStore = readStoreRep . unStore

-- | Write to a 'Store'.
writeStore :: (MonadComp m, Storable m a) => Store m a -> a -> m ()
writeStore = writeStoreRep . unStore

-- | Unsafe freezeing of a 'Store'. This operation is only safe if the 'Store'
--   is not updated as long as the returned value is alive.
unsafeFreezeStore :: (MonadComp m, Storable m a) => Store m a -> m a
unsafeFreezeStore = unsafeFreezeStoreRep . unStore

-- | Update a 'Store' in-place, that is, a update that won't produce any
--   temporary variable to store values read from the 'Store'.
inplace :: (MonadComp m, Storable m a) => Store m a -> (a -> a) -> m ()
inplace store f = writeStore store . f =<< unsafeFreezeStore store

--------------------------------------------------------------------------------