{-# LANGUAGE ExistentialQuantification #-}

module Heart.Core.Alloc
  ( Alloc
  , newAlloc
  , newEnumAlloc
  , incAlloc
  ) where

import Heart.Core.Prelude
import UnliftIO.IORef

data Alloc e = forall x. Alloc
  { _allocStep :: !(x -> x)
  , _allocRef:: !(IORef x)
  , _allocExtract :: !(x -> e)
  }

instance Functor Alloc where
  fmap f (Alloc step ref extract) = Alloc step ref (f . extract)

newAlloc :: MonadIO m => (e -> e) -> e -> m (Alloc e)
newAlloc f e = fmap (\r -> Alloc f r id) (newIORef e)

newEnumAlloc :: (MonadIO m, Enum e) => m (Alloc e)
newEnumAlloc = newAlloc succ (toEnum 0)

incAlloc :: MonadIO m => Alloc e -> m e
incAlloc (Alloc step ref extract) = atomicModifyIORef' ref (\e -> (step e, extract e))