packages feed

primitive-extras-0.5.0.2: test/Main/Transaction.hs

module Main.Transaction where

import Prelude
import Focus (Focus(..))
import qualified Focus
import qualified PrimitiveExtras.SparseSmallArray as SparseSmallArray
import qualified Data.Text as Text
import qualified DeferredFolds.Unfold as Unfold


data Transaction element = forall result. (Show result, Eq result) => Transaction {
  name :: Text,
  applyToMaybeList :: State [Maybe element] result,
  applyToSparseSmallArray :: State (SparseSmallArray.SparseSmallArray element) result
}

instance Show (Transaction element) where
  show = Text.unpack . name

singleton :: Show element => Int -> element -> Transaction element
singleton index element =
  Transaction {
    name = "singleton " <> (fromString . show) index <> " " <> fromString (show element)
    ,
    applyToMaybeList =
      put $ map (\i' -> if index == i' then Just element else Nothing) [0 .. pred (finiteBitSize (undefined :: Int))]
    ,
    applyToSparseSmallArray =
      put $ SparseSmallArray.singleton index element
  }

set :: Show element => Int -> element -> Transaction element
set index element =
  Transaction {
    name = "set " <> (fromString . show) index <> " " <> (fromString . show) element
    ,
    applyToMaybeList = do
      l <- get
      put $ do
        (i', e') <- zip [0..] l
        return $ if index == i' then Just element else e'
    ,
    applyToSparseSmallArray = do
      ssa <- get
      case SparseSmallArray.lookup index ssa of
        Just _ -> put (SparseSmallArray.replace index element ssa)
        Nothing -> put (SparseSmallArray.insert index element ssa)
  }

unset :: Int -> Transaction element
unset index =
  Transaction {
    name = "unset " <> fromString (show index)
    ,
    applyToMaybeList = do
      l <- get
      put $ do
        (i', e') <- zip [0..] l
        return $ if index == i' then Nothing else e'
    ,
    applyToSparseSmallArray =
      get >>= put . (SparseSmallArray.unset index)
  }

lookup :: (Show element, Eq element) => Int -> Transaction element
lookup index =
  Transaction {
    name = "lookup " <> fromString (show index),
    applyToMaybeList = fmap (join . fmap fst . uncons . drop index) get,
    applyToSparseSmallArray = fmap (SparseSmallArray.lookup index) get
  }

elementsUnfold :: (Show element, Eq element) => Transaction element
elementsUnfold =
  Transaction {
    name = "elementsUnfold",
    applyToMaybeList = do
      list <- get
      return $ do
        maybeElement <- Unfold.foldable list
        element <- Unfold.foldable maybeElement
        return element,
    applyToSparseSmallArray = fmap SparseSmallArray.elementsUnfold get
  }

focusAt :: (Show element, Eq result, Show result) => Text -> Focus element Identity result -> Int -> Transaction element
focusAt focusName focus index =
  Transaction {
    name = "focusAt (" <> (fromString . show) index <> " " <> focusName <> ")"
    ,
    applyToMaybeList = do
      list <- get
      case focus of
        Focus conceal reveal -> case splitAt index list of
          (precedingList, elementMaybe : trailingList) -> do
            (result, change) <- lift (maybe conceal reveal elementMaybe)
            case change of
              Focus.Leave -> return ()
              Focus.Set newElement -> put (precedingList <> [Just newElement] <> trailingList)
              Focus.Remove -> put (precedingList <> [Nothing] <> trailingList)
            return result
          _ -> error "Index out of bounds"
    ,
    applyToSparseSmallArray = StateT (SparseSmallArray.focusAt focus index)
  }

focusInsert :: Show element => Int -> element -> Transaction element
focusInsert index element =
  focusAt ("Insert (" <> (fromString . show) element <> ")") (Focus.insert element) index

focusDelete :: Show element => Int -> Transaction element
focusDelete index =
  focusAt ("Delete") Focus.delete index