packages feed

seqn-0.1.0.0: src/Data/Seqn/Internal/Util.hs

-- |
-- This is an internal module. You probably don't need to import this. Use
-- "Data.Seqn.Seq", "Data.Seqn.MSeq", or "Data.Seqn.PQueue" instead.
--
-- The only reason to use this module is to use the constructs defined here with
-- other internal modules.
--
module Data.Seqn.Internal.Util
  ( Biapplicative(..)
  , S2(..)
  , S3(..)
  , SMaybe(..)
  , Tagged(..)
  , SStateT(..)
  , evalSStateT
  , SState
  , sState
  , evalSState
  , (#.)
  ) where

import qualified Control.Applicative -- for before liftA2 in Prelude
import Data.Bifunctor (Bifunctor(..))
import Data.Coerce (Coercible, coerce)
import Data.Functor.Const (Const(..))
import Data.Functor.Identity (Identity(..))

class Bifunctor p => Biapplicative p where
  bipure :: a -> b -> p a b
  biliftA2 :: (a -> b -> c) -> (d -> e -> f) -> p a d -> p b e -> p c f

instance Biapplicative Const where
  bipure x _ = coerce x
  biliftA2 f _ = coerce f

data S2 a b = S2 !a !b

instance Functor (S2 a) where
  fmap f (S2 x y) = S2 x (f y)

instance Bifunctor S2 where
  bimap f g (S2 x y) = S2 (f x) (g y)

instance Biapplicative S2 where
  bipure = S2
  biliftA2 f g (S2 x1 y1) (S2 x2 y2) = S2 (f x1 x2) (g y1 y2)

data S3 a b c = S3 !a !b !c

data SMaybe a
  = SNothing
  | SJust !a

newtype Tagged a b = Tagged { unTagged :: b }

instance Functor (Tagged a) where
  fmap = coerce

instance Bifunctor Tagged where
  bimap _ = coerce

instance Biapplicative Tagged where
  bipure _ = coerce
  biliftA2 _ = coerce

-- Strict in the state, value, and bind.
newtype SStateT s m a = SStateT { runSStateT :: s -> m (S2 s a) }

evalSStateT :: Functor m => SStateT s m a -> s -> m a
evalSStateT m s = fmap (\(S2 _ x) -> x) (runSStateT m s)
{-# INLINE evalSStateT #-}

type SState s = SStateT s Identity

sState :: (s -> S2 s a) -> SState s a
sState = coerce

evalSState :: SState s a -> s -> a
evalSState m s = case runSStateT m s of Identity (S2 _ x) -> x
{-# INLINE evalSState #-}

instance Functor m => Functor (SStateT s m) where
  fmap f m = SStateT $ \s -> (fmap . fmap) f (runSStateT m s)
  {-# INLINE fmap #-}

instance Monad m => Applicative (SStateT s m) where
  pure x = SStateT $ \s -> pure $ S2 s x
  {-# INLINE pure #-}

  liftA2 f m1 m2 = SStateT $ \s -> do
    S2 s1 x <- runSStateT m1 s
    S2 s2 y <- runSStateT m2 s1
    pure $ S2 s2 (f x y)
  {-# INLINE liftA2 #-}

-- Borrow a trick from base in case GHC has trouble optimizing certain
-- function coercions.
--
-- See Note [Function coercion] in
-- https://gitlab.haskell.org/ghc/ghc/-/blob/8d67f247c3e4ca3810712654e1becbf927405f6b/libraries/ghc-internal/src/GHC/Internal/Data/Functor/Utils.hs#L134-160
(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c)
(#.) _ = coerce
{-# INLINE (#.) #-}

infixr 9 #.