{-# LANGUAGE CPP #-}
{-# LANGUAGE FunctionalDependencies #-}
#if MIN_VERSION_base(4,8,0)
{-# LANGUAGE Safe #-}
#else
{-# LANGUAGE Trustworthy #-}
#endif

module Data.Strict.Classes (
    Strict (..),
) where

import Prelude ((.))
import qualified Prelude as L
import qualified Data.These as L

import Data.Strict.These
import Data.Strict.Tuple
import Data.Strict.Maybe
import Data.Strict.Either

import qualified Control.Monad.ST.Lazy as L
import qualified Control.Monad.ST.Strict as S
import qualified Control.Monad.Trans.RWS.Lazy as L
import qualified Control.Monad.Trans.RWS.Strict as S
import qualified Control.Monad.Trans.State.Lazy as L
import qualified Control.Monad.Trans.State.Strict as S
import qualified Control.Monad.Trans.Writer.Lazy as L
import qualified Control.Monad.Trans.Writer.Strict as S
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT

-- | Ad hoc conversion between "strict" and "lazy" versions of a structure.
--
-- Unfortunately all externally defined instances are doomed to
-- be orphans: https://gitlab.haskell.org/ghc/ghc/-/issues/11999
-- See also https://qfpl.io/posts/orphans-and-fundeps/index.html for
--
class Strict lazy strict | lazy -> strict, strict -> lazy where
  toStrict :: lazy -> strict
  toLazy   :: strict -> lazy

instance Strict (L.Maybe a) (Maybe a) where
  toStrict :: Maybe a -> Maybe a
toStrict Maybe a
L.Nothing  = forall a. Maybe a
Nothing
  toStrict (L.Just a
x) = forall a. a -> Maybe a
Just a
x
  
  toLazy :: Maybe a -> Maybe a
toLazy Maybe a
Nothing  = forall a. Maybe a
L.Nothing
  toLazy (Just a
x) = forall a. a -> Maybe a
L.Just a
x

instance Strict (a, b) (Pair a b) where
  toStrict :: (a, b) -> Pair a b
toStrict (a
a, b
b) = a
a forall a b. a -> b -> Pair a b
:!: b
b
  toLazy :: Pair a b -> (a, b)
toLazy (a
a :!: b
b) = (a
a, b
b)

instance Strict (L.Either a b) (Either a b) where
  toStrict :: Either a b -> Either a b
toStrict (L.Left a
x)  = forall a b. a -> Either a b
Left a
x
  toStrict (L.Right b
y) = forall a b. b -> Either a b
Right b
y
  
  toLazy :: Either a b -> Either a b
toLazy (Left a
x)  = forall a b. a -> Either a b
L.Left a
x
  toLazy (Right b
y) = forall a b. b -> Either a b
L.Right b
y

instance Strict (L.These a b) (These a b) where
  toStrict :: These a b -> These a b
toStrict (L.This a
x)    = forall a b. a -> These a b
This a
x
  toStrict (L.That b
y)    = forall a b. b -> These a b
That b
y
  toStrict (L.These a
x b
y) = forall a b. a -> b -> These a b
These a
x b
y
  
  toLazy :: These a b -> These a b
toLazy (This a
x)    = forall a b. a -> These a b
L.This a
x
  toLazy (That b
y)    = forall a b. b -> These a b
L.That b
y
  toLazy (These a
x b
y) = forall a b. a -> b -> These a b
L.These a
x b
y

instance Strict LBS.ByteString BS.ByteString where
#if MIN_VERSION_bytestring(0,10,0)
  toStrict :: ByteString -> ByteString
toStrict = ByteString -> ByteString
LBS.toStrict
  toLazy :: ByteString -> ByteString
toLazy   = ByteString -> ByteString
LBS.fromStrict
#else
  toStrict = BS.concat . LBS.toChunks
  toLazy   = LBS.fromChunks . L.return {- singleton -}
#endif

instance Strict LT.Text T.Text where
  toStrict :: Text -> Text
toStrict = Text -> Text
LT.toStrict
  toLazy :: Text -> Text
toLazy   = Text -> Text
LT.fromStrict

instance Strict (L.ST s a) (S.ST s a) where
  toStrict :: ST s a -> ST s a
toStrict = forall s a. ST s a -> ST s a
L.lazyToStrictST
  toLazy :: ST s a -> ST s a
toLazy   = forall s a. ST s a -> ST s a
L.strictToLazyST

instance Strict (L.RWST r w s m a) (S.RWST r w s m a) where
  toStrict :: RWST r w s m a -> RWST r w s m a
toStrict = forall r w s (m :: * -> *) a.
(r -> s -> m (a, s, w)) -> RWST r w s m a
S.RWST forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall r w s (m :: * -> *) a.
RWST r w s m a -> r -> s -> m (a, s, w)
L.runRWST
  toLazy :: RWST r w s m a -> RWST r w s m a
toLazy   = forall r w s (m :: * -> *) a.
(r -> s -> m (a, s, w)) -> RWST r w s m a
L.RWST forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall r w s (m :: * -> *) a.
RWST r w s m a -> r -> s -> m (a, s, w)
S.runRWST

instance Strict (L.StateT s m a) (S.StateT s m a) where
  toStrict :: StateT s m a -> StateT s m a
toStrict = forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
S.StateT forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
L.runStateT
  toLazy :: StateT s m a -> StateT s m a
toLazy   = forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
L.StateT forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
S.runStateT

instance Strict (L.WriterT w m a) (S.WriterT w m a) where
  toStrict :: WriterT w m a -> WriterT w m a
toStrict = forall w (m :: * -> *) a. m (a, w) -> WriterT w m a
S.WriterT forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
L.runWriterT
  toLazy :: WriterT w m a -> WriterT w m a
toLazy   = forall w (m :: * -> *) a. m (a, w) -> WriterT w m a
L.WriterT forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
S.runWriterT