spectacle-1.0.0: src/Control/Comonad/Tape.hs
module Control.Comonad.Tape
( -- * Tape Comonad
Tape (Tape, before, focus, after),
-- ** Construction
viewl,
viewr,
viewAt,
-- ** Destruction
toSeq,
-- ** Operations
shiftl,
shiftr,
tabulater,
tabulatel,
)
where
import Control.Comonad (Comonad, duplicate, extend, extract)
import Control.Comonad.Store (ComonadStore, peek, pos, seek)
import Data.Sequence (Seq (Empty, (:<|), (:|>)), (<|), (|>))
import qualified Data.Sequence as Seq
-- ---------------------------------------------------------------------------------------------------------------------
data Tape a = Tape {before :: Seq a, focus :: a, after :: Seq a}
deriving (Eq, Functor, Show)
-- | @since 1.0.0
instance Foldable Tape where
foldr cons nil = foldr cons nil . toSeq
{-# INLINE foldr #-}
-- | @since 1.0.0
instance Traversable Tape where
traverse f (Tape lower x upper) = Tape <$> traverse f lower <*> f x <*> traverse f upper
{-# INLINE traverse #-}
-- | @since 1.0.0
instance Comonad Tape where
extract (Tape _ x _) = x
{-# INLINE extract #-}
duplicate tp = Tape (tabulatel tp) tp (tabulater tp)
{-# INLINE duplicate #-}
extend f tp = case duplicate tp of
Tape ls x us -> Tape (f <$> ls) (f x) (f <$> us)
-- | @since 1.0.0
instance ComonadStore Int Tape where
pos (Tape lw _ _) = length lw
{-# INLINE pos #-}
peek n
| n < 0 = extract . shiftl n
| n > 0 = extract . shiftr n
| otherwise = extract
{-# INLINE peek #-}
seek n
| n < 0 = shiftl n
| n > 0 = shiftr n
| otherwise = id
{-# INLINE seek #-}
-- ---------------------------------------------------------------------------------------------------------------------
-- | /O(1)/, @'viewl' xs@ constructs a 'Tape' by viewing @xs@ from the left, if it is nonempty.
--
-- @since 1.0.0
viewl :: Seq a -> Maybe (Tape a)
viewl Empty = Nothing
viewl (x :<| upper) = Just (Tape mempty x upper)
-- | /O(1)/, @'viewl' xs@ constructs a 'Tape' by viewing @xs@ from the left, if it is nonempty.
--
-- @since 1.0.0
viewr :: Seq a -> Maybe (Tape a)
viewr Empty = Nothing
viewr (lower :|> x) = Just (Tape lower x mempty)
-- | /O(log n)/, @'viewAt' i xs@ constructs a 'Tape' focusing the ith element of @xs@, if it is nonempty. @i@ is
-- clamped to the interval [0, i).
--
-- @since 1.0.0
viewAt :: Int -> Seq a -> Maybe (Tape a)
viewAt i xs =
case Seq.splitAt i xs of
(Empty, Empty) -> Nothing
(lower :|> x, Empty) -> Just (Tape lower x Empty)
(lower, x :<| upper) -> Just (Tape lower x upper)
toSeq :: Tape a -> Seq a
toSeq (Tape lower x upper) = lower <> (x <| upper)
shiftl :: Int -> Tape a -> Tape a
shiftl i (Tape lw0 x0 up0) =
case Seq.splitAt (length lw0 - abs i) lw0 of
(_, Empty) -> Tape lw0 x0 up0
(lw, x :<| up) -> Tape lw x (up <> (x0 <| up0))
shiftr :: Int -> Tape a -> Tape a
shiftr i (Tape lw0 x0 up0) =
case Seq.splitAt (abs i) up0 of
(Empty, _) -> Tape lw0 x0 up0
(lw :|> x, up) -> Tape (lw0 <> (x0 <| lw)) x up
tabulatel :: Tape a -> Seq (Tape a)
tabulatel sp@(Tape lower _ _)
| Seq.null lower = Seq.empty
| otherwise = tabulatel (shiftl 1 sp) |> shiftl 1 sp
tabulater :: Tape a -> Seq (Tape a)
tabulater sp@(Tape _ _ up)
| Seq.null up = Seq.empty
| otherwise = shiftr 1 sp <| tabulater (shiftr 1 sp)