packages feed

ghci4luatex-0.0: src/Data/Memoizer/Commands.hs

--------------------------------------------------------------------------------
-- |
--
-- Module      :  Data.Memoizer.Commands
-- Description :  A memoizer for sequences of commands
-- Copyright   :  (c) Alice Rixte 2024
-- License     :  BSD 3
-- Maintainer  :  alice.rixte@u-bordeaux.fr
-- Stability   :  unstable
-- Portability :  portable
--
-- A data structure to memoize the result of the execution of a sequence of
-- commands.
--
-- As long as the sequence of commands is identical to the one memoized, we can
-- avoid executing them. As soon as there is one command that differs from the
-- memoized sequence, then we should discard all remaining memoized results.
--
-- = Usage
--
--
-- Let's store the result of some commands (we alternate between @memo@ and
-- @memo'@ to avoid recursive definitions)
--
-- >>> import Prelude hiding (lookup)
-- >>> memo' = storeResult "x=1" "" empty :: CmdMemoizer String String
-- >>> memo = storeResult "y=2" "" memo'
-- >>> memo' = storeResult "x+y" "3" memo
--
--
--
-- Suppose there are no more commands in the sequence. Now we want to execute
-- that sequence of commands again but some commands may have changed in
-- between. We use memoized commands as long as all commands are equal to the
-- previous ones:
--
-- >>> memo = restart memo'
-- >>> lookup "x=1" memo
-- Just ""
--
-- Since the command was memoized, we avoid executing is again. Now suppose the command @"y=2"@ was replaced by @"y=3"@
--
-- >>> memo' = nextCmd memo
-- >>> lookup "y=3" memo'
-- Nothing
--
-- Since the command was not memoized, we have to execute it:
--
-- >>> memo = storeResult "y=3" "" memo'
--
-- Now none of the subsequent commands will use the memoized version:
--
-- >>> memo' = nextCmd memo
-- >>> lookup "x+y" memo'
-- Nothing
--
--------------------------------------------------------------------------------

module Data.Memoizer.Commands
  ( CmdMemoizer
  , empty
  , storeResult
  , deleteResult
  , lookup
  , restart
  , nextCmd
  )
where


import Prelude hiding (lookup)

import qualified Data.IntMap as Map

-- | A container for the memoized result of the execution of a sequence of
-- commands
--
-- * @a@ is the type of commands
-- * @b@ is the type of results
--
data CmdMemoizer a b = CmdMemoizer
  { memoizerMap :: Map.IntMap (a,b)
  , currentIndex :: Int
  , foundModif :: Bool
  }
  deriving (Show, Eq)

-- | Memoizer of an empty sequence of commands
--
empty :: CmdMemoizer a b
empty = CmdMemoizer Map.empty 0 False

-- | Restart the sequence of commands
--
-- Memoized results will now be accessible until @'storeResult'@ or
-- @'deleteResult'@ are used.
--
restart :: CmdMemoizer a b -> CmdMemoizer a b
restart m = m {currentIndex = 0, foundModif = False}


-- | Store the result of the execution of a command.
--
-- This will override the current memoized command, and prevent access to
-- any memoized result until @'restart'@ is used.
--
storeResult :: a -> b -> CmdMemoizer a b -> CmdMemoizer a b
storeResult a b (CmdMemoizer m i _) =
  CmdMemoizer (Map.insert i (a,b) m) (i+1) True

-- | Delete the result of the current memoized command.
--
-- This will override the current memoized command, and prevent access to any
-- memoized result until @'restart'@ is used.
--
deleteResult :: CmdMemoizer a b -> CmdMemoizer a b
deleteResult (CmdMemoizer m i _) = CmdMemoizer (Map.delete i m) (i+1) True


-- | Access the memoized result of a command if it is equal to the current
-- memoized command
--
lookup :: Eq a => a -> CmdMemoizer a b -> Maybe b
lookup a (CmdMemoizer m i modif) =
  if modif then
    Nothing
  else
    case Map.lookup i m of
      Nothing -> Nothing
      Just (a', b) ->
        if a == a' then
          Just b
        else
          Nothing

-- | Move to the next memoized command
--
nextCmd :: CmdMemoizer a b -> CmdMemoizer a b
nextCmd m = m {currentIndex = currentIndex m + 1 }