packages feed

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

{-# OPTIONS_GHC -Wno-redundant-constraints #-}

module Main.Transaction where

import qualified Data.Text as Text
import qualified DeferredFolds.Unfoldl as Unfoldl
import Focus (Focus (..))
import qualified Focus
import qualified PrimitiveExtras.By6Bits as By6Bits
import Prelude

data Transaction element
  = forall result.
  (Show result, Eq result) =>
  Transaction
  { name :: Text,
    applyToMaybeList :: State [Maybe element] result,
    applyToBy6Bits :: State (By6Bits.By6Bits 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))],
      applyToBy6Bits =
        put $ By6Bits.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',
      applyToBy6Bits = do
        ssa <- get
        case By6Bits.lookup index ssa of
          Just _ -> put (By6Bits.replace index element ssa)
          Nothing -> put (By6Bits.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',
      applyToBy6Bits =
        get >>= put . (By6Bits.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,
      applyToBy6Bits = fmap (By6Bits.lookup index) get
    }

elementsUnfoldl :: (Show element, Eq element) => Transaction element
elementsUnfoldl =
  Transaction
    { name = "elementsUnfoldl",
      applyToMaybeList = do
        list <- get
        return $ do
          maybeElement <- Unfoldl.foldable list
          element <- Unfoldl.foldable maybeElement
          return element,
      applyToBy6Bits = fmap By6Bits.elementsUnfoldl 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",
      applyToBy6Bits = StateT (By6Bits.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